roojs-debug.js
[roojs1] / roojs-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
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88
89         /**
90          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
91          * @type Boolean
92          */
93         enableGarbageCollector : true,
94
95         /**
96          * True to automatically purge event listeners after uncaching an element (defaults to false).
97          * Note: this only happens if enableGarbageCollector is true.
98          * @type Boolean
99          */
100         enableListenerCollection:false,
101
102         /**
103          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
104          * the IE insecure content warning (defaults to javascript:false).
105          * @type String
106          */
107         SSL_SECURE_URL : "javascript:false",
108
109         /**
110          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
111          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
112          * @type String
113          */
114         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
115
116         emptyFn : function(){},
117
118         /**
119          * Copies all the properties of config to obj if they don't already exist.
120          * @param {Object} obj The receiver of the properties
121          * @param {Object} config The source of the properties
122          * @return {Object} returns obj
123          */
124         applyIf : function(o, c){
125             if(o && c){
126                 for(var p in c){
127                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
128                 }
129             }
130             return o;
131         },
132
133         /**
134          * Applies event listeners to elements by selectors when the document is ready.
135          * The event name is specified with an @ suffix.
136 <pre><code>
137 Roo.addBehaviors({
138    // add a listener for click on all anchors in element with id foo
139    '#foo a@click' : function(e, t){
140        // do something
141    },
142
143    // add the same listener to multiple selectors (separated by comma BEFORE the @)
144    '#foo a, #bar span.some-class@mouseover' : function(){
145        // do something
146    }
147 });
148 </code></pre>
149          * @param {Object} obj The list of behaviors to apply
150          */
151         addBehaviors : function(o){
152             if(!Roo.isReady){
153                 Roo.onReady(function(){
154                     Roo.addBehaviors(o);
155                 });
156                 return;
157             }
158             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
159             for(var b in o){
160                 var parts = b.split('@');
161                 if(parts[1]){ // for Object prototype breakers
162                     var s = parts[0];
163                     if(!cache[s]){
164                         cache[s] = Roo.select(s);
165                     }
166                     cache[s].on(parts[1], o[b]);
167                 }
168             }
169             cache = null;
170         },
171
172         /**
173          * Generates unique ids. If the element already has an id, it is unchanged
174          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
175          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
176          * @return {String} The generated Id.
177          */
178         id : function(el, prefix){
179             prefix = prefix || "roo-gen";
180             el = Roo.getDom(el);
181             var id = prefix + (++idSeed);
182             return el ? (el.id ? el.id : (el.id = id)) : id;
183         },
184          
185        
186         /**
187          * Extends one class with another class and optionally overrides members with the passed literal. This class
188          * also adds the function "override()" to the class that can be used to override
189          * members on an instance.
190          * @param {Object} subclass The class inheriting the functionality
191          * @param {Object} superclass The class being extended
192          * @param {Object} overrides (optional) A literal with members
193          * @method extend
194          */
195         extend : function(){
196             // inline overrides
197             var io = function(o){
198                 for(var m in o){
199                     this[m] = o[m];
200                 }
201             };
202             return function(sb, sp, overrides){
203                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
204                     overrides = sp;
205                     sp = sb;
206                     sb = function(){sp.apply(this, arguments);};
207                 }
208                 var F = function(){}, sbp, spp = sp.prototype;
209                 F.prototype = spp;
210                 sbp = sb.prototype = new F();
211                 sbp.constructor=sb;
212                 sb.superclass=spp;
213                 
214                 if(spp.constructor == Object.prototype.constructor){
215                     spp.constructor=sp;
216                    
217                 }
218                 
219                 sb.override = function(o){
220                     Roo.override(sb, o);
221                 };
222                 sbp.override = io;
223                 Roo.override(sb, overrides);
224                 return sb;
225             };
226         }(),
227
228         /**
229          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
230          * Usage:<pre><code>
231 Roo.override(MyClass, {
232     newMethod1: function(){
233         // etc.
234     },
235     newMethod2: function(foo){
236         // etc.
237     }
238 });
239  </code></pre>
240          * @param {Object} origclass The class to override
241          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
242          * containing one or more methods.
243          * @method override
244          */
245         override : function(origclass, overrides){
246             if(overrides){
247                 var p = origclass.prototype;
248                 for(var method in overrides){
249                     p[method] = overrides[method];
250                 }
251             }
252         },
253         /**
254          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
255          * <pre><code>
256 Roo.namespace('Company', 'Company.data');
257 Company.Widget = function() { ... }
258 Company.data.CustomStore = function(config) { ... }
259 </code></pre>
260          * @param {String} namespace1
261          * @param {String} namespace2
262          * @param {String} etc
263          * @method namespace
264          */
265         namespace : function(){
266             var a=arguments, o=null, i, j, d, rt;
267             for (i=0; i<a.length; ++i) {
268                 d=a[i].split(".");
269                 rt = d[0];
270                 /** eval:var:o */
271                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
272                 for (j=1; j<d.length; ++j) {
273                     o[d[j]]=o[d[j]] || {};
274                     o=o[d[j]];
275                 }
276             }
277         },
278         /**
279          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
280          * <pre><code>
281 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
282 Roo.factory(conf, Roo.data);
283 </code></pre>
284          * @param {String} classname
285          * @param {String} namespace (optional)
286          * @method factory
287          */
288          
289         factory : function(c, ns)
290         {
291             // no xtype, no ns or c.xns - or forced off by c.xns
292             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
293                 return c;
294             }
295             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
296             if (c.constructor == ns[c.xtype]) {// already created...
297                 return c;
298             }
299             if (ns[c.xtype]) {
300                 console.log("Roo.Factory(" + c.xtype + ")");
301                 var ret = new ns[c.xtype](c);
302                 ret.xns = false;
303                 return ret;
304             }
305             c.xns = false; // prevent recursion..
306             return c;
307         },
308          
309         /**
310          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
311          * @param {Object} o
312          * @return {String}
313          */
314         urlEncode : function(o){
315             if(!o){
316                 return "";
317             }
318             var buf = [];
319             for(var key in o){
320                 var ov = o[key], k = encodeURIComponent(key);
321                 var type = typeof ov;
322                 if(type == 'undefined'){
323                     buf.push(k, "=&");
324                 }else if(type != "function" && type != "object"){
325                     buf.push(k, "=", encodeURIComponent(ov), "&");
326                 }else if(ov instanceof Array){
327                     if (ov.length) {
328                             for(var i = 0, len = ov.length; i < len; i++) {
329                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
330                             }
331                         } else {
332                             buf.push(k, "=&");
333                         }
334                 }
335             }
336             buf.pop();
337             return buf.join("");
338         },
339
340         /**
341          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
342          * @param {String} string
343          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
344          * @return {Object} A literal with members
345          */
346         urlDecode : function(string, overwrite){
347             if(!string || !string.length){
348                 return {};
349             }
350             var obj = {};
351             var pairs = string.split('&');
352             var pair, name, value;
353             for(var i = 0, len = pairs.length; i < len; i++){
354                 pair = pairs[i].split('=');
355                 name = decodeURIComponent(pair[0]);
356                 value = decodeURIComponent(pair[1]);
357                 if(overwrite !== true){
358                     if(typeof obj[name] == "undefined"){
359                         obj[name] = value;
360                     }else if(typeof obj[name] == "string"){
361                         obj[name] = [obj[name]];
362                         obj[name].push(value);
363                     }else{
364                         obj[name].push(value);
365                     }
366                 }else{
367                     obj[name] = value;
368                 }
369             }
370             return obj;
371         },
372
373         /**
374          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
375          * passed array is not really an array, your function is called once with it.
376          * The supplied function is called with (Object item, Number index, Array allItems).
377          * @param {Array/NodeList/Mixed} array
378          * @param {Function} fn
379          * @param {Object} scope
380          */
381         each : function(array, fn, scope){
382             if(typeof array.length == "undefined" || typeof array == "string"){
383                 array = [array];
384             }
385             for(var i = 0, len = array.length; i < len; i++){
386                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
387             }
388         },
389
390         // deprecated
391         combine : function(){
392             var as = arguments, l = as.length, r = [];
393             for(var i = 0; i < l; i++){
394                 var a = as[i];
395                 if(a instanceof Array){
396                     r = r.concat(a);
397                 }else if(a.length !== undefined && !a.substr){
398                     r = r.concat(Array.prototype.slice.call(a, 0));
399                 }else{
400                     r.push(a);
401                 }
402             }
403             return r;
404         },
405
406         /**
407          * Escapes the passed string for use in a regular expression
408          * @param {String} str
409          * @return {String}
410          */
411         escapeRe : function(s) {
412             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
413         },
414
415         // internal
416         callback : function(cb, scope, args, delay){
417             if(typeof cb == "function"){
418                 if(delay){
419                     cb.defer(delay, scope, args || []);
420                 }else{
421                     cb.apply(scope, args || []);
422                 }
423             }
424         },
425
426         /**
427          * Return the dom node for the passed string (id), dom node, or Roo.Element
428          * @param {String/HTMLElement/Roo.Element} el
429          * @return HTMLElement
430          */
431         getDom : function(el){
432             if(!el){
433                 return null;
434             }
435             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
436         },
437
438         /**
439         * Shorthand for {@link Roo.ComponentMgr#get}
440         * @param {String} id
441         * @return Roo.Component
442         */
443         getCmp : function(id){
444             return Roo.ComponentMgr.get(id);
445         },
446          
447         num : function(v, defaultValue){
448             if(typeof v != 'number'){
449                 return defaultValue;
450             }
451             return v;
452         },
453
454         destroy : function(){
455             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
456                 var as = a[i];
457                 if(as){
458                     if(as.dom){
459                         as.removeAllListeners();
460                         as.remove();
461                         continue;
462                     }
463                     if(typeof as.purgeListeners == 'function'){
464                         as.purgeListeners();
465                     }
466                     if(typeof as.destroy == 'function'){
467                         as.destroy();
468                     }
469                 }
470             }
471         },
472
473         // inpired by a similar function in mootools library
474         /**
475          * Returns the type of object that is passed in. If the object passed in is null or undefined it
476          * return false otherwise it returns one of the following values:<ul>
477          * <li><b>string</b>: If the object passed is a string</li>
478          * <li><b>number</b>: If the object passed is a number</li>
479          * <li><b>boolean</b>: If the object passed is a boolean value</li>
480          * <li><b>function</b>: If the object passed is a function reference</li>
481          * <li><b>object</b>: If the object passed is an object</li>
482          * <li><b>array</b>: If the object passed is an array</li>
483          * <li><b>regexp</b>: If the object passed is a regular expression</li>
484          * <li><b>element</b>: If the object passed is a DOM Element</li>
485          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
486          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
487          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
488          * @param {Mixed} object
489          * @return {String}
490          */
491         type : function(o){
492             if(o === undefined || o === null){
493                 return false;
494             }
495             if(o.htmlElement){
496                 return 'element';
497             }
498             var t = typeof o;
499             if(t == 'object' && o.nodeName) {
500                 switch(o.nodeType) {
501                     case 1: return 'element';
502                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
503                 }
504             }
505             if(t == 'object' || t == 'function') {
506                 switch(o.constructor) {
507                     case Array: return 'array';
508                     case RegExp: return 'regexp';
509                 }
510                 if(typeof o.length == 'number' && typeof o.item == 'function') {
511                     return 'nodelist';
512                 }
513             }
514             return t;
515         },
516
517         /**
518          * Returns true if the passed value is null, undefined or an empty string (optional).
519          * @param {Mixed} value The value to test
520          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
521          * @return {Boolean}
522          */
523         isEmpty : function(v, allowBlank){
524             return v === null || v === undefined || (!allowBlank ? v === '' : false);
525         },
526         
527         /** @type Boolean */
528         isOpera : isOpera,
529         /** @type Boolean */
530         isSafari : isSafari,
531         /** @type Boolean */
532         isIE : isIE,
533         /** @type Boolean */
534         isIE7 : isIE7,
535         /** @type Boolean */
536         isGecko : isGecko,
537         /** @type Boolean */
538         isBorderBox : isBorderBox,
539         /** @type Boolean */
540         isWindows : isWindows,
541         /** @type Boolean */
542         isLinux : isLinux,
543         /** @type Boolean */
544         isMac : isMac,
545
546         /**
547          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
548          * you may want to set this to true.
549          * @type Boolean
550          */
551         useShims : ((isIE && !isIE7) || (isGecko && isMac))
552     });
553
554
555 })();
556
557 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
558                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
559 /*
560  * Based on:
561  * Ext JS Library 1.1.1
562  * Copyright(c) 2006-2007, Ext JS, LLC.
563  *
564  * Originally Released Under LGPL - original licence link has changed is not relivant.
565  *
566  * Fork - LGPL
567  * <script type="text/javascript">
568  */
569
570 (function() {    
571     // wrappedn so fnCleanup is not in global scope...
572     if(Roo.isIE) {
573         function fnCleanUp() {
574             var p = Function.prototype;
575             delete p.createSequence;
576             delete p.defer;
577             delete p.createDelegate;
578             delete p.createCallback;
579             delete p.createInterceptor;
580
581             window.detachEvent("onunload", fnCleanUp);
582         }
583         window.attachEvent("onunload", fnCleanUp);
584     }
585 })();
586
587
588 /**
589  * @class Function
590  * These functions are available on every Function object (any JavaScript function).
591  */
592 Roo.apply(Function.prototype, {
593      /**
594      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
595      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
596      * Will create a function that is bound to those 2 args.
597      * @return {Function} The new function
598     */
599     createCallback : function(/*args...*/){
600         // make args available, in function below
601         var args = arguments;
602         var method = this;
603         return function() {
604             return method.apply(window, args);
605         };
606     },
607
608     /**
609      * Creates a delegate (callback) that sets the scope to obj.
610      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
611      * Will create a function that is automatically scoped to this.
612      * @param {Object} obj (optional) The object for which the scope is set
613      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
614      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
615      *                                             if a number the args are inserted at the specified position
616      * @return {Function} The new function
617      */
618     createDelegate : function(obj, args, appendArgs){
619         var method = this;
620         return function() {
621             var callArgs = args || arguments;
622             if(appendArgs === true){
623                 callArgs = Array.prototype.slice.call(arguments, 0);
624                 callArgs = callArgs.concat(args);
625             }else if(typeof appendArgs == "number"){
626                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
627                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
628                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
629             }
630             return method.apply(obj || window, callArgs);
631         };
632     },
633
634     /**
635      * Calls this function after the number of millseconds specified.
636      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
637      * @param {Object} obj (optional) The object for which the scope is set
638      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
639      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
640      *                                             if a number the args are inserted at the specified position
641      * @return {Number} The timeout id that can be used with clearTimeout
642      */
643     defer : function(millis, obj, args, appendArgs){
644         var fn = this.createDelegate(obj, args, appendArgs);
645         if(millis){
646             return setTimeout(fn, millis);
647         }
648         fn();
649         return 0;
650     },
651     /**
652      * Create a combined function call sequence of the original function + the passed function.
653      * The resulting function returns the results of the original function.
654      * The passed fcn is called with the parameters of the original function
655      * @param {Function} fcn The function to sequence
656      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
657      * @return {Function} The new function
658      */
659     createSequence : function(fcn, scope){
660         if(typeof fcn != "function"){
661             return this;
662         }
663         var method = this;
664         return function() {
665             var retval = method.apply(this || window, arguments);
666             fcn.apply(scope || this || window, arguments);
667             return retval;
668         };
669     },
670
671     /**
672      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
673      * The resulting function returns the results of the original function.
674      * The passed fcn is called with the parameters of the original function.
675      * @addon
676      * @param {Function} fcn The function to call before the original
677      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
678      * @return {Function} The new function
679      */
680     createInterceptor : function(fcn, scope){
681         if(typeof fcn != "function"){
682             return this;
683         }
684         var method = this;
685         return function() {
686             fcn.target = this;
687             fcn.method = method;
688             if(fcn.apply(scope || this || window, arguments) === false){
689                 return;
690             }
691             return method.apply(this || window, arguments);
692         };
693     }
694 });
695 /*
696  * Based on:
697  * Ext JS Library 1.1.1
698  * Copyright(c) 2006-2007, Ext JS, LLC.
699  *
700  * Originally Released Under LGPL - original licence link has changed is not relivant.
701  *
702  * Fork - LGPL
703  * <script type="text/javascript">
704  */
705
706 Roo.applyIf(String, {
707     
708     /** @scope String */
709     
710     /**
711      * Escapes the passed string for ' and \
712      * @param {String} string The string to escape
713      * @return {String} The escaped string
714      * @static
715      */
716     escape : function(string) {
717         return string.replace(/('|\\)/g, "\\$1");
718     },
719
720     /**
721      * Pads the left side of a string with a specified character.  This is especially useful
722      * for normalizing number and date strings.  Example usage:
723      * <pre><code>
724 var s = String.leftPad('123', 5, '0');
725 // s now contains the string: '00123'
726 </code></pre>
727      * @param {String} string The original string
728      * @param {Number} size The total length of the output string
729      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
730      * @return {String} The padded string
731      * @static
732      */
733     leftPad : function (val, size, ch) {
734         var result = new String(val);
735         if(ch === null || ch === undefined || ch === '') {
736             ch = " ";
737         }
738         while (result.length < size) {
739             result = ch + result;
740         }
741         return result;
742     },
743
744     /**
745      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
746      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
747      * <pre><code>
748 var cls = 'my-class', text = 'Some text';
749 var s = String.format('<div class="{0}">{1}</div>', cls, text);
750 // s now contains the string: '<div class="my-class">Some text</div>'
751 </code></pre>
752      * @param {String} string The tokenized string to be formatted
753      * @param {String} value1 The value to replace token {0}
754      * @param {String} value2 Etc...
755      * @return {String} The formatted string
756      * @static
757      */
758     format : function(format){
759         var args = Array.prototype.slice.call(arguments, 1);
760         return format.replace(/\{(\d+)\}/g, function(m, i){
761             return Roo.util.Format.htmlEncode(args[i]);
762         });
763     }
764 });
765
766 /**
767  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
768  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
769  * they are already different, the first value passed in is returned.  Note that this method returns the new value
770  * but does not change the current string.
771  * <pre><code>
772 // alternate sort directions
773 sort = sort.toggle('ASC', 'DESC');
774
775 // instead of conditional logic:
776 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
777 </code></pre>
778  * @param {String} value The value to compare to the current string
779  * @param {String} other The new value to use if the string already equals the first value passed in
780  * @return {String} The new value
781  */
782  
783 String.prototype.toggle = function(value, other){
784     return this == value ? other : value;
785 };/*
786  * Based on:
787  * Ext JS Library 1.1.1
788  * Copyright(c) 2006-2007, Ext JS, LLC.
789  *
790  * Originally Released Under LGPL - original licence link has changed is not relivant.
791  *
792  * Fork - LGPL
793  * <script type="text/javascript">
794  */
795
796  /**
797  * @class Number
798  */
799 Roo.applyIf(Number.prototype, {
800     /**
801      * Checks whether or not the current number is within a desired range.  If the number is already within the
802      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
803      * exceeded.  Note that this method returns the constrained value but does not change the current number.
804      * @param {Number} min The minimum number in the range
805      * @param {Number} max The maximum number in the range
806      * @return {Number} The constrained value if outside the range, otherwise the current value
807      */
808     constrain : function(min, max){
809         return Math.min(Math.max(this, min), max);
810     }
811 });/*
812  * Based on:
813  * Ext JS Library 1.1.1
814  * Copyright(c) 2006-2007, Ext JS, LLC.
815  *
816  * Originally Released Under LGPL - original licence link has changed is not relivant.
817  *
818  * Fork - LGPL
819  * <script type="text/javascript">
820  */
821  /**
822  * @class Array
823  */
824 Roo.applyIf(Array.prototype, {
825     /**
826      * Checks whether or not the specified object exists in the array.
827      * @param {Object} o The object to check for
828      * @return {Number} The index of o in the array (or -1 if it is not found)
829      */
830     indexOf : function(o){
831        for (var i = 0, len = this.length; i < len; i++){
832               if(this[i] == o) return i;
833        }
834            return -1;
835     },
836
837     /**
838      * Removes the specified object from the array.  If the object is not found nothing happens.
839      * @param {Object} o The object to remove
840      */
841     remove : function(o){
842        var index = this.indexOf(o);
843        if(index != -1){
844            this.splice(index, 1);
845        }
846     }
847 });/*
848  * Based on:
849  * Ext JS Library 1.1.1
850  * Copyright(c) 2006-2007, Ext JS, LLC.
851  *
852  * Originally Released Under LGPL - original licence link has changed is not relivant.
853  *
854  * Fork - LGPL
855  * <script type="text/javascript">
856  */
857
858 /**
859  * @class Date
860  *
861  * The date parsing and format syntax is a subset of
862  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
863  * supported will provide results equivalent to their PHP versions.
864  *
865  * Following is the list of all currently supported formats:
866  *<pre>
867 Sample date:
868 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
869
870 Format  Output      Description
871 ------  ----------  --------------------------------------------------------------
872   d      10         Day of the month, 2 digits with leading zeros
873   D      Wed        A textual representation of a day, three letters
874   j      10         Day of the month without leading zeros
875   l      Wednesday  A full textual representation of the day of the week
876   S      th         English ordinal day of month suffix, 2 chars (use with j)
877   w      3          Numeric representation of the day of the week
878   z      9          The julian date, or day of the year (0-365)
879   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
880   F      January    A full textual representation of the month
881   m      01         Numeric representation of a month, with leading zeros
882   M      Jan        Month name abbreviation, three letters
883   n      1          Numeric representation of a month, without leading zeros
884   t      31         Number of days in the given month
885   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
886   Y      2007       A full numeric representation of a year, 4 digits
887   y      07         A two digit representation of a year
888   a      pm         Lowercase Ante meridiem and Post meridiem
889   A      PM         Uppercase Ante meridiem and Post meridiem
890   g      3          12-hour format of an hour without leading zeros
891   G      15         24-hour format of an hour without leading zeros
892   h      03         12-hour format of an hour with leading zeros
893   H      15         24-hour format of an hour with leading zeros
894   i      05         Minutes with leading zeros
895   s      01         Seconds, with leading zeros
896   O      -0600      Difference to Greenwich time (GMT) in hours
897   T      CST        Timezone setting of the machine running the code
898   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
899 </pre>
900  *
901  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
902  * <pre><code>
903 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
904 document.write(dt.format('Y-m-d'));                         //2007-01-10
905 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
906 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
907  </code></pre>
908  *
909  * Here are some standard date/time patterns that you might find helpful.  They
910  * are not part of the source of Date.js, but to use them you can simply copy this
911  * block of code into any script that is included after Date.js and they will also become
912  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
913  * <pre><code>
914 Date.patterns = {
915     ISO8601Long:"Y-m-d H:i:s",
916     ISO8601Short:"Y-m-d",
917     ShortDate: "n/j/Y",
918     LongDate: "l, F d, Y",
919     FullDateTime: "l, F d, Y g:i:s A",
920     MonthDay: "F d",
921     ShortTime: "g:i A",
922     LongTime: "g:i:s A",
923     SortableDateTime: "Y-m-d\\TH:i:s",
924     UniversalSortableDateTime: "Y-m-d H:i:sO",
925     YearMonth: "F, Y"
926 };
927 </code></pre>
928  *
929  * Example usage:
930  * <pre><code>
931 var dt = new Date();
932 document.write(dt.format(Date.patterns.ShortDate));
933  </code></pre>
934  */
935
936 /*
937  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
938  * They generate precompiled functions from date formats instead of parsing and
939  * processing the pattern every time you format a date.  These functions are available
940  * on every Date object (any javascript function).
941  *
942  * The original article and download are here:
943  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
944  *
945  */
946  
947  
948  // was in core
949 /**
950  Returns the number of milliseconds between this date and date
951  @param {Date} date (optional) Defaults to now
952  @return {Number} The diff in milliseconds
953  @member Date getElapsed
954  */
955 Date.prototype.getElapsed = function(date) {
956         return Math.abs((date || new Date()).getTime()-this.getTime());
957 };
958 // was in date file..
959
960
961 // private
962 Date.parseFunctions = {count:0};
963 // private
964 Date.parseRegexes = [];
965 // private
966 Date.formatFunctions = {count:0};
967
968 // private
969 Date.prototype.dateFormat = function(format) {
970     if (Date.formatFunctions[format] == null) {
971         Date.createNewFormat(format);
972     }
973     var func = Date.formatFunctions[format];
974     return this[func]();
975 };
976
977
978 /**
979  * Formats a date given the supplied format string
980  * @param {String} format The format string
981  * @return {String} The formatted date
982  * @method
983  */
984 Date.prototype.format = Date.prototype.dateFormat;
985
986 // private
987 Date.createNewFormat = function(format) {
988     var funcName = "format" + Date.formatFunctions.count++;
989     Date.formatFunctions[format] = funcName;
990     var code = "Date.prototype." + funcName + " = function(){return ";
991     var special = false;
992     var ch = '';
993     for (var i = 0; i < format.length; ++i) {
994         ch = format.charAt(i);
995         if (!special && ch == "\\") {
996             special = true;
997         }
998         else if (special) {
999             special = false;
1000             code += "'" + String.escape(ch) + "' + ";
1001         }
1002         else {
1003             code += Date.getFormatCode(ch);
1004         }
1005     }
1006     /** eval:var:zzzzzzzzzzzzz */
1007     eval(code.substring(0, code.length - 3) + ";}");
1008 };
1009
1010 // private
1011 Date.getFormatCode = function(character) {
1012     switch (character) {
1013     case "d":
1014         return "String.leftPad(this.getDate(), 2, '0') + ";
1015     case "D":
1016         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1017     case "j":
1018         return "this.getDate() + ";
1019     case "l":
1020         return "Date.dayNames[this.getDay()] + ";
1021     case "S":
1022         return "this.getSuffix() + ";
1023     case "w":
1024         return "this.getDay() + ";
1025     case "z":
1026         return "this.getDayOfYear() + ";
1027     case "W":
1028         return "this.getWeekOfYear() + ";
1029     case "F":
1030         return "Date.monthNames[this.getMonth()] + ";
1031     case "m":
1032         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1033     case "M":
1034         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1035     case "n":
1036         return "(this.getMonth() + 1) + ";
1037     case "t":
1038         return "this.getDaysInMonth() + ";
1039     case "L":
1040         return "(this.isLeapYear() ? 1 : 0) + ";
1041     case "Y":
1042         return "this.getFullYear() + ";
1043     case "y":
1044         return "('' + this.getFullYear()).substring(2, 4) + ";
1045     case "a":
1046         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1047     case "A":
1048         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1049     case "g":
1050         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1051     case "G":
1052         return "this.getHours() + ";
1053     case "h":
1054         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1055     case "H":
1056         return "String.leftPad(this.getHours(), 2, '0') + ";
1057     case "i":
1058         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1059     case "s":
1060         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1061     case "O":
1062         return "this.getGMTOffset() + ";
1063     case "T":
1064         return "this.getTimezone() + ";
1065     case "Z":
1066         return "(this.getTimezoneOffset() * -60) + ";
1067     default:
1068         return "'" + String.escape(character) + "' + ";
1069     }
1070 };
1071
1072 /**
1073  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1074  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1075  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1076  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1077  * string or the parse operation will fail.
1078  * Example Usage:
1079 <pre><code>
1080 //dt = Fri May 25 2007 (current date)
1081 var dt = new Date();
1082
1083 //dt = Thu May 25 2006 (today's month/day in 2006)
1084 dt = Date.parseDate("2006", "Y");
1085
1086 //dt = Sun Jan 15 2006 (all date parts specified)
1087 dt = Date.parseDate("2006-1-15", "Y-m-d");
1088
1089 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1090 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1091 </code></pre>
1092  * @param {String} input The unparsed date as a string
1093  * @param {String} format The format the date is in
1094  * @return {Date} The parsed date
1095  * @static
1096  */
1097 Date.parseDate = function(input, format) {
1098     if (Date.parseFunctions[format] == null) {
1099         Date.createParser(format);
1100     }
1101     var func = Date.parseFunctions[format];
1102     return Date[func](input);
1103 };
1104
1105 // private
1106 Date.createParser = function(format) {
1107     var funcName = "parse" + Date.parseFunctions.count++;
1108     var regexNum = Date.parseRegexes.length;
1109     var currentGroup = 1;
1110     Date.parseFunctions[format] = funcName;
1111
1112     var code = "Date." + funcName + " = function(input){\n"
1113         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1114         + "var d = new Date();\n"
1115         + "y = d.getFullYear();\n"
1116         + "m = d.getMonth();\n"
1117         + "d = d.getDate();\n"
1118         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1119         + "if (results && results.length > 0) {";
1120     var regex = "";
1121
1122     var special = false;
1123     var ch = '';
1124     for (var i = 0; i < format.length; ++i) {
1125         ch = format.charAt(i);
1126         if (!special && ch == "\\") {
1127             special = true;
1128         }
1129         else if (special) {
1130             special = false;
1131             regex += String.escape(ch);
1132         }
1133         else {
1134             var obj = Date.formatCodeToRegex(ch, currentGroup);
1135             currentGroup += obj.g;
1136             regex += obj.s;
1137             if (obj.g && obj.c) {
1138                 code += obj.c;
1139             }
1140         }
1141     }
1142
1143     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1144         + "{v = new Date(y, m, d, h, i, s);}\n"
1145         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1146         + "{v = new Date(y, m, d, h, i);}\n"
1147         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1148         + "{v = new Date(y, m, d, h);}\n"
1149         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1150         + "{v = new Date(y, m, d);}\n"
1151         + "else if (y >= 0 && m >= 0)\n"
1152         + "{v = new Date(y, m);}\n"
1153         + "else if (y >= 0)\n"
1154         + "{v = new Date(y);}\n"
1155         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1156         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1157         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1158         + ";}";
1159
1160     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1161      /** eval:var:zzzzzzzzzzzzz */
1162     eval(code);
1163 };
1164
1165 // private
1166 Date.formatCodeToRegex = function(character, currentGroup) {
1167     switch (character) {
1168     case "D":
1169         return {g:0,
1170         c:null,
1171         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1172     case "j":
1173         return {g:1,
1174             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1175             s:"(\\d{1,2})"}; // day of month without leading zeroes
1176     case "d":
1177         return {g:1,
1178             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1179             s:"(\\d{2})"}; // day of month with leading zeroes
1180     case "l":
1181         return {g:0,
1182             c:null,
1183             s:"(?:" + Date.dayNames.join("|") + ")"};
1184     case "S":
1185         return {g:0,
1186             c:null,
1187             s:"(?:st|nd|rd|th)"};
1188     case "w":
1189         return {g:0,
1190             c:null,
1191             s:"\\d"};
1192     case "z":
1193         return {g:0,
1194             c:null,
1195             s:"(?:\\d{1,3})"};
1196     case "W":
1197         return {g:0,
1198             c:null,
1199             s:"(?:\\d{2})"};
1200     case "F":
1201         return {g:1,
1202             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1203             s:"(" + Date.monthNames.join("|") + ")"};
1204     case "M":
1205         return {g:1,
1206             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1207             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1208     case "n":
1209         return {g:1,
1210             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1211             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1212     case "m":
1213         return {g:1,
1214             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1215             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1216     case "t":
1217         return {g:0,
1218             c:null,
1219             s:"\\d{1,2}"};
1220     case "L":
1221         return {g:0,
1222             c:null,
1223             s:"(?:1|0)"};
1224     case "Y":
1225         return {g:1,
1226             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1227             s:"(\\d{4})"};
1228     case "y":
1229         return {g:1,
1230             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1231                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1232             s:"(\\d{1,2})"};
1233     case "a":
1234         return {g:1,
1235             c:"if (results[" + currentGroup + "] == 'am') {\n"
1236                 + "if (h == 12) { h = 0; }\n"
1237                 + "} else { if (h < 12) { h += 12; }}",
1238             s:"(am|pm)"};
1239     case "A":
1240         return {g:1,
1241             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1242                 + "if (h == 12) { h = 0; }\n"
1243                 + "} else { if (h < 12) { h += 12; }}",
1244             s:"(AM|PM)"};
1245     case "g":
1246     case "G":
1247         return {g:1,
1248             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1249             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1250     case "h":
1251     case "H":
1252         return {g:1,
1253             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1254             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1255     case "i":
1256         return {g:1,
1257             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1258             s:"(\\d{2})"};
1259     case "s":
1260         return {g:1,
1261             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1262             s:"(\\d{2})"};
1263     case "O":
1264         return {g:1,
1265             c:[
1266                 "o = results[", currentGroup, "];\n",
1267                 "var sn = o.substring(0,1);\n", // get + / - sign
1268                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1269                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1270                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1271                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1272             ].join(""),
1273             s:"([+\-]\\d{4})"};
1274     case "T":
1275         return {g:0,
1276             c:null,
1277             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1278     case "Z":
1279         return {g:1,
1280             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1281                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1282             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1283     default:
1284         return {g:0,
1285             c:null,
1286             s:String.escape(character)};
1287     }
1288 };
1289
1290 /**
1291  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1292  * @return {String} The abbreviated timezone name (e.g. 'CST')
1293  */
1294 Date.prototype.getTimezone = function() {
1295     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1296 };
1297
1298 /**
1299  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1300  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1301  */
1302 Date.prototype.getGMTOffset = function() {
1303     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1304         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1305         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1306 };
1307
1308 /**
1309  * Get the numeric day number of the year, adjusted for leap year.
1310  * @return {Number} 0 through 364 (365 in leap years)
1311  */
1312 Date.prototype.getDayOfYear = function() {
1313     var num = 0;
1314     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1315     for (var i = 0; i < this.getMonth(); ++i) {
1316         num += Date.daysInMonth[i];
1317     }
1318     return num + this.getDate() - 1;
1319 };
1320
1321 /**
1322  * Get the string representation of the numeric week number of the year
1323  * (equivalent to the format specifier 'W').
1324  * @return {String} '00' through '52'
1325  */
1326 Date.prototype.getWeekOfYear = function() {
1327     // Skip to Thursday of this week
1328     var now = this.getDayOfYear() + (4 - this.getDay());
1329     // Find the first Thursday of the year
1330     var jan1 = new Date(this.getFullYear(), 0, 1);
1331     var then = (7 - jan1.getDay() + 4);
1332     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1333 };
1334
1335 /**
1336  * Whether or not the current date is in a leap year.
1337  * @return {Boolean} True if the current date is in a leap year, else false
1338  */
1339 Date.prototype.isLeapYear = function() {
1340     var year = this.getFullYear();
1341     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1342 };
1343
1344 /**
1345  * Get the first day of the current month, adjusted for leap year.  The returned value
1346  * is the numeric day index within the week (0-6) which can be used in conjunction with
1347  * the {@link #monthNames} array to retrieve the textual day name.
1348  * Example:
1349  *<pre><code>
1350 var dt = new Date('1/10/2007');
1351 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1352 </code></pre>
1353  * @return {Number} The day number (0-6)
1354  */
1355 Date.prototype.getFirstDayOfMonth = function() {
1356     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1357     return (day < 0) ? (day + 7) : day;
1358 };
1359
1360 /**
1361  * Get the last day of the current month, adjusted for leap year.  The returned value
1362  * is the numeric day index within the week (0-6) which can be used in conjunction with
1363  * the {@link #monthNames} array to retrieve the textual day name.
1364  * Example:
1365  *<pre><code>
1366 var dt = new Date('1/10/2007');
1367 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1368 </code></pre>
1369  * @return {Number} The day number (0-6)
1370  */
1371 Date.prototype.getLastDayOfMonth = function() {
1372     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1373     return (day < 0) ? (day + 7) : day;
1374 };
1375
1376
1377 /**
1378  * Get the first date of this date's month
1379  * @return {Date}
1380  */
1381 Date.prototype.getFirstDateOfMonth = function() {
1382     return new Date(this.getFullYear(), this.getMonth(), 1);
1383 };
1384
1385 /**
1386  * Get the last date of this date's month
1387  * @return {Date}
1388  */
1389 Date.prototype.getLastDateOfMonth = function() {
1390     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1391 };
1392 /**
1393  * Get the number of days in the current month, adjusted for leap year.
1394  * @return {Number} The number of days in the month
1395  */
1396 Date.prototype.getDaysInMonth = function() {
1397     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1398     return Date.daysInMonth[this.getMonth()];
1399 };
1400
1401 /**
1402  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1403  * @return {String} 'st, 'nd', 'rd' or 'th'
1404  */
1405 Date.prototype.getSuffix = function() {
1406     switch (this.getDate()) {
1407         case 1:
1408         case 21:
1409         case 31:
1410             return "st";
1411         case 2:
1412         case 22:
1413             return "nd";
1414         case 3:
1415         case 23:
1416             return "rd";
1417         default:
1418             return "th";
1419     }
1420 };
1421
1422 // private
1423 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1424
1425 /**
1426  * An array of textual month names.
1427  * Override these values for international dates, for example...
1428  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1429  * @type Array
1430  * @static
1431  */
1432 Date.monthNames =
1433    ["January",
1434     "February",
1435     "March",
1436     "April",
1437     "May",
1438     "June",
1439     "July",
1440     "August",
1441     "September",
1442     "October",
1443     "November",
1444     "December"];
1445
1446 /**
1447  * An array of textual day names.
1448  * Override these values for international dates, for example...
1449  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1450  * @type Array
1451  * @static
1452  */
1453 Date.dayNames =
1454    ["Sunday",
1455     "Monday",
1456     "Tuesday",
1457     "Wednesday",
1458     "Thursday",
1459     "Friday",
1460     "Saturday"];
1461
1462 // private
1463 Date.y2kYear = 50;
1464 // private
1465 Date.monthNumbers = {
1466     Jan:0,
1467     Feb:1,
1468     Mar:2,
1469     Apr:3,
1470     May:4,
1471     Jun:5,
1472     Jul:6,
1473     Aug:7,
1474     Sep:8,
1475     Oct:9,
1476     Nov:10,
1477     Dec:11};
1478
1479 /**
1480  * Creates and returns a new Date instance with the exact same date value as the called instance.
1481  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1482  * variable will also be changed.  When the intention is to create a new variable that will not
1483  * modify the original instance, you should create a clone.
1484  *
1485  * Example of correctly cloning a date:
1486  * <pre><code>
1487 //wrong way:
1488 var orig = new Date('10/1/2006');
1489 var copy = orig;
1490 copy.setDate(5);
1491 document.write(orig);  //returns 'Thu Oct 05 2006'!
1492
1493 //correct way:
1494 var orig = new Date('10/1/2006');
1495 var copy = orig.clone();
1496 copy.setDate(5);
1497 document.write(orig);  //returns 'Thu Oct 01 2006'
1498 </code></pre>
1499  * @return {Date} The new Date instance
1500  */
1501 Date.prototype.clone = function() {
1502         return new Date(this.getTime());
1503 };
1504
1505 /**
1506  * Clears any time information from this date
1507  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1508  @return {Date} this or the clone
1509  */
1510 Date.prototype.clearTime = function(clone){
1511     if(clone){
1512         return this.clone().clearTime();
1513     }
1514     this.setHours(0);
1515     this.setMinutes(0);
1516     this.setSeconds(0);
1517     this.setMilliseconds(0);
1518     return this;
1519 };
1520
1521 // private
1522 // safari setMonth is broken
1523 if(Roo.isSafari){
1524     Date.brokenSetMonth = Date.prototype.setMonth;
1525         Date.prototype.setMonth = function(num){
1526                 if(num <= -1){
1527                         var n = Math.ceil(-num);
1528                         var back_year = Math.ceil(n/12);
1529                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1530                         this.setFullYear(this.getFullYear() - back_year);
1531                         return Date.brokenSetMonth.call(this, month);
1532                 } else {
1533                         return Date.brokenSetMonth.apply(this, arguments);
1534                 }
1535         };
1536 }
1537
1538 /** Date interval constant 
1539 * @static 
1540 * @type String */
1541 Date.MILLI = "ms";
1542 /** Date interval constant 
1543 * @static 
1544 * @type String */
1545 Date.SECOND = "s";
1546 /** Date interval constant 
1547 * @static 
1548 * @type String */
1549 Date.MINUTE = "mi";
1550 /** Date interval constant 
1551 * @static 
1552 * @type String */
1553 Date.HOUR = "h";
1554 /** Date interval constant 
1555 * @static 
1556 * @type String */
1557 Date.DAY = "d";
1558 /** Date interval constant 
1559 * @static 
1560 * @type String */
1561 Date.MONTH = "mo";
1562 /** Date interval constant 
1563 * @static 
1564 * @type String */
1565 Date.YEAR = "y";
1566
1567 /**
1568  * Provides a convenient method of performing basic date arithmetic.  This method
1569  * does not modify the Date instance being called - it creates and returns
1570  * a new Date instance containing the resulting date value.
1571  *
1572  * Examples:
1573  * <pre><code>
1574 //Basic usage:
1575 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1576 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1577
1578 //Negative values will subtract correctly:
1579 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1580 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1581
1582 //You can even chain several calls together in one line!
1583 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1584 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1585  </code></pre>
1586  *
1587  * @param {String} interval   A valid date interval enum value
1588  * @param {Number} value      The amount to add to the current date
1589  * @return {Date} The new Date instance
1590  */
1591 Date.prototype.add = function(interval, value){
1592   var d = this.clone();
1593   if (!interval || value === 0) return d;
1594   switch(interval.toLowerCase()){
1595     case Date.MILLI:
1596       d.setMilliseconds(this.getMilliseconds() + value);
1597       break;
1598     case Date.SECOND:
1599       d.setSeconds(this.getSeconds() + value);
1600       break;
1601     case Date.MINUTE:
1602       d.setMinutes(this.getMinutes() + value);
1603       break;
1604     case Date.HOUR:
1605       d.setHours(this.getHours() + value);
1606       break;
1607     case Date.DAY:
1608       d.setDate(this.getDate() + value);
1609       break;
1610     case Date.MONTH:
1611       var day = this.getDate();
1612       if(day > 28){
1613           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1614       }
1615       d.setDate(day);
1616       d.setMonth(this.getMonth() + value);
1617       break;
1618     case Date.YEAR:
1619       d.setFullYear(this.getFullYear() + value);
1620       break;
1621   }
1622   return d;
1623 };/*
1624  * Based on:
1625  * Ext JS Library 1.1.1
1626  * Copyright(c) 2006-2007, Ext JS, LLC.
1627  *
1628  * Originally Released Under LGPL - original licence link has changed is not relivant.
1629  *
1630  * Fork - LGPL
1631  * <script type="text/javascript">
1632  */
1633
1634 Roo.lib.Dom = {
1635     getViewWidth : function(full) {
1636         return full ? this.getDocumentWidth() : this.getViewportWidth();
1637     },
1638
1639     getViewHeight : function(full) {
1640         return full ? this.getDocumentHeight() : this.getViewportHeight();
1641     },
1642
1643     getDocumentHeight: function() {
1644         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1645         return Math.max(scrollHeight, this.getViewportHeight());
1646     },
1647
1648     getDocumentWidth: function() {
1649         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1650         return Math.max(scrollWidth, this.getViewportWidth());
1651     },
1652
1653     getViewportHeight: function() {
1654         var height = self.innerHeight;
1655         var mode = document.compatMode;
1656
1657         if ((mode || Roo.isIE) && !Roo.isOpera) {
1658             height = (mode == "CSS1Compat") ?
1659                      document.documentElement.clientHeight :
1660                      document.body.clientHeight;
1661         }
1662
1663         return height;
1664     },
1665
1666     getViewportWidth: function() {
1667         var width = self.innerWidth;
1668         var mode = document.compatMode;
1669
1670         if (mode || Roo.isIE) {
1671             width = (mode == "CSS1Compat") ?
1672                     document.documentElement.clientWidth :
1673                     document.body.clientWidth;
1674         }
1675         return width;
1676     },
1677
1678     isAncestor : function(p, c) {
1679         p = Roo.getDom(p);
1680         c = Roo.getDom(c);
1681         if (!p || !c) {
1682             return false;
1683         }
1684
1685         if (p.contains && !Roo.isSafari) {
1686             return p.contains(c);
1687         } else if (p.compareDocumentPosition) {
1688             return !!(p.compareDocumentPosition(c) & 16);
1689         } else {
1690             var parent = c.parentNode;
1691             while (parent) {
1692                 if (parent == p) {
1693                     return true;
1694                 }
1695                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1696                     return false;
1697                 }
1698                 parent = parent.parentNode;
1699             }
1700             return false;
1701         }
1702     },
1703
1704     getRegion : function(el) {
1705         return Roo.lib.Region.getRegion(el);
1706     },
1707
1708     getY : function(el) {
1709         return this.getXY(el)[1];
1710     },
1711
1712     getX : function(el) {
1713         return this.getXY(el)[0];
1714     },
1715
1716     getXY : function(el) {
1717         var p, pe, b, scroll, bd = document.body;
1718         el = Roo.getDom(el);
1719         var fly = Roo.lib.AnimBase.fly;
1720         if (el.getBoundingClientRect) {
1721             b = el.getBoundingClientRect();
1722             scroll = fly(document).getScroll();
1723             return [b.left + scroll.left, b.top + scroll.top];
1724         }
1725         var x = 0, y = 0;
1726
1727         p = el;
1728
1729         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1730
1731         while (p) {
1732
1733             x += p.offsetLeft;
1734             y += p.offsetTop;
1735
1736             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1737                 hasAbsolute = true;
1738             }
1739
1740             if (Roo.isGecko) {
1741                 pe = fly(p);
1742
1743                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1744                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1745
1746
1747                 x += bl;
1748                 y += bt;
1749
1750
1751                 if (p != el && pe.getStyle('overflow') != 'visible') {
1752                     x += bl;
1753                     y += bt;
1754                 }
1755             }
1756             p = p.offsetParent;
1757         }
1758
1759         if (Roo.isSafari && hasAbsolute) {
1760             x -= bd.offsetLeft;
1761             y -= bd.offsetTop;
1762         }
1763
1764         if (Roo.isGecko && !hasAbsolute) {
1765             var dbd = fly(bd);
1766             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1767             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1768         }
1769
1770         p = el.parentNode;
1771         while (p && p != bd) {
1772             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1773                 x -= p.scrollLeft;
1774                 y -= p.scrollTop;
1775             }
1776             p = p.parentNode;
1777         }
1778         return [x, y];
1779     },
1780  
1781   
1782
1783
1784     setXY : function(el, xy) {
1785         el = Roo.fly(el, '_setXY');
1786         el.position();
1787         var pts = el.translatePoints(xy);
1788         if (xy[0] !== false) {
1789             el.dom.style.left = pts.left + "px";
1790         }
1791         if (xy[1] !== false) {
1792             el.dom.style.top = pts.top + "px";
1793         }
1794     },
1795
1796     setX : function(el, x) {
1797         this.setXY(el, [x, false]);
1798     },
1799
1800     setY : function(el, y) {
1801         this.setXY(el, [false, y]);
1802     }
1803 };
1804 /*
1805  * Portions of this file are based on pieces of Yahoo User Interface Library
1806  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1807  * YUI licensed under the BSD License:
1808  * http://developer.yahoo.net/yui/license.txt
1809  * <script type="text/javascript">
1810  *
1811  */
1812
1813 Roo.lib.Event = function() {
1814     var loadComplete = false;
1815     var listeners = [];
1816     var unloadListeners = [];
1817     var retryCount = 0;
1818     var onAvailStack = [];
1819     var counter = 0;
1820     var lastError = null;
1821
1822     return {
1823         POLL_RETRYS: 200,
1824         POLL_INTERVAL: 20,
1825         EL: 0,
1826         TYPE: 1,
1827         FN: 2,
1828         WFN: 3,
1829         OBJ: 3,
1830         ADJ_SCOPE: 4,
1831         _interval: null,
1832
1833         startInterval: function() {
1834             if (!this._interval) {
1835                 var self = this;
1836                 var callback = function() {
1837                     self._tryPreloadAttach();
1838                 };
1839                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1840
1841             }
1842         },
1843
1844         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1845             onAvailStack.push({ id:         p_id,
1846                 fn:         p_fn,
1847                 obj:        p_obj,
1848                 override:   p_override,
1849                 checkReady: false    });
1850
1851             retryCount = this.POLL_RETRYS;
1852             this.startInterval();
1853         },
1854
1855
1856         addListener: function(el, eventName, fn) {
1857             el = Roo.getDom(el);
1858             if (!el || !fn) {
1859                 return false;
1860             }
1861
1862             if ("unload" == eventName) {
1863                 unloadListeners[unloadListeners.length] =
1864                 [el, eventName, fn];
1865                 return true;
1866             }
1867
1868             var wrappedFn = function(e) {
1869                 return fn(Roo.lib.Event.getEvent(e));
1870             };
1871
1872             var li = [el, eventName, fn, wrappedFn];
1873
1874             var index = listeners.length;
1875             listeners[index] = li;
1876
1877             this.doAdd(el, eventName, wrappedFn, false);
1878             return true;
1879
1880         },
1881
1882
1883         removeListener: function(el, eventName, fn) {
1884             var i, len;
1885
1886             el = Roo.getDom(el);
1887
1888             if(!fn) {
1889                 return this.purgeElement(el, false, eventName);
1890             }
1891
1892
1893             if ("unload" == eventName) {
1894
1895                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1896                     var li = unloadListeners[i];
1897                     if (li &&
1898                         li[0] == el &&
1899                         li[1] == eventName &&
1900                         li[2] == fn) {
1901                         unloadListeners.splice(i, 1);
1902                         return true;
1903                     }
1904                 }
1905
1906                 return false;
1907             }
1908
1909             var cacheItem = null;
1910
1911
1912             var index = arguments[3];
1913
1914             if ("undefined" == typeof index) {
1915                 index = this._getCacheIndex(el, eventName, fn);
1916             }
1917
1918             if (index >= 0) {
1919                 cacheItem = listeners[index];
1920             }
1921
1922             if (!el || !cacheItem) {
1923                 return false;
1924             }
1925
1926             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1927
1928             delete listeners[index][this.WFN];
1929             delete listeners[index][this.FN];
1930             listeners.splice(index, 1);
1931
1932             return true;
1933
1934         },
1935
1936
1937         getTarget: function(ev, resolveTextNode) {
1938             ev = ev.browserEvent || ev;
1939             var t = ev.target || ev.srcElement;
1940             return this.resolveTextNode(t);
1941         },
1942
1943
1944         resolveTextNode: function(node) {
1945             if (Roo.isSafari && node && 3 == node.nodeType) {
1946                 return node.parentNode;
1947             } else {
1948                 return node;
1949             }
1950         },
1951
1952
1953         getPageX: function(ev) {
1954             ev = ev.browserEvent || ev;
1955             var x = ev.pageX;
1956             if (!x && 0 !== x) {
1957                 x = ev.clientX || 0;
1958
1959                 if (Roo.isIE) {
1960                     x += this.getScroll()[1];
1961                 }
1962             }
1963
1964             return x;
1965         },
1966
1967
1968         getPageY: function(ev) {
1969             ev = ev.browserEvent || ev;
1970             var y = ev.pageY;
1971             if (!y && 0 !== y) {
1972                 y = ev.clientY || 0;
1973
1974                 if (Roo.isIE) {
1975                     y += this.getScroll()[0];
1976                 }
1977             }
1978
1979
1980             return y;
1981         },
1982
1983
1984         getXY: function(ev) {
1985             ev = ev.browserEvent || ev;
1986             return [this.getPageX(ev), this.getPageY(ev)];
1987         },
1988
1989
1990         getRelatedTarget: function(ev) {
1991             ev = ev.browserEvent || ev;
1992             var t = ev.relatedTarget;
1993             if (!t) {
1994                 if (ev.type == "mouseout") {
1995                     t = ev.toElement;
1996                 } else if (ev.type == "mouseover") {
1997                     t = ev.fromElement;
1998                 }
1999             }
2000
2001             return this.resolveTextNode(t);
2002         },
2003
2004
2005         getTime: function(ev) {
2006             ev = ev.browserEvent || ev;
2007             if (!ev.time) {
2008                 var t = new Date().getTime();
2009                 try {
2010                     ev.time = t;
2011                 } catch(ex) {
2012                     this.lastError = ex;
2013                     return t;
2014                 }
2015             }
2016
2017             return ev.time;
2018         },
2019
2020
2021         stopEvent: function(ev) {
2022             this.stopPropagation(ev);
2023             this.preventDefault(ev);
2024         },
2025
2026
2027         stopPropagation: function(ev) {
2028             ev = ev.browserEvent || ev;
2029             if (ev.stopPropagation) {
2030                 ev.stopPropagation();
2031             } else {
2032                 ev.cancelBubble = true;
2033             }
2034         },
2035
2036
2037         preventDefault: function(ev) {
2038             ev = ev.browserEvent || ev;
2039             if(ev.preventDefault) {
2040                 ev.preventDefault();
2041             } else {
2042                 ev.returnValue = false;
2043             }
2044         },
2045
2046
2047         getEvent: function(e) {
2048             var ev = e || window.event;
2049             if (!ev) {
2050                 var c = this.getEvent.caller;
2051                 while (c) {
2052                     ev = c.arguments[0];
2053                     if (ev && Event == ev.constructor) {
2054                         break;
2055                     }
2056                     c = c.caller;
2057                 }
2058             }
2059             return ev;
2060         },
2061
2062
2063         getCharCode: function(ev) {
2064             ev = ev.browserEvent || ev;
2065             return ev.charCode || ev.keyCode || 0;
2066         },
2067
2068
2069         _getCacheIndex: function(el, eventName, fn) {
2070             for (var i = 0,len = listeners.length; i < len; ++i) {
2071                 var li = listeners[i];
2072                 if (li &&
2073                     li[this.FN] == fn &&
2074                     li[this.EL] == el &&
2075                     li[this.TYPE] == eventName) {
2076                     return i;
2077                 }
2078             }
2079
2080             return -1;
2081         },
2082
2083
2084         elCache: {},
2085
2086
2087         getEl: function(id) {
2088             return document.getElementById(id);
2089         },
2090
2091
2092         clearCache: function() {
2093         },
2094
2095
2096         _load: function(e) {
2097             loadComplete = true;
2098             var EU = Roo.lib.Event;
2099
2100
2101             if (Roo.isIE) {
2102                 EU.doRemove(window, "load", EU._load);
2103             }
2104         },
2105
2106
2107         _tryPreloadAttach: function() {
2108
2109             if (this.locked) {
2110                 return false;
2111             }
2112
2113             this.locked = true;
2114
2115
2116             var tryAgain = !loadComplete;
2117             if (!tryAgain) {
2118                 tryAgain = (retryCount > 0);
2119             }
2120
2121
2122             var notAvail = [];
2123             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2124                 var item = onAvailStack[i];
2125                 if (item) {
2126                     var el = this.getEl(item.id);
2127
2128                     if (el) {
2129                         if (!item.checkReady ||
2130                             loadComplete ||
2131                             el.nextSibling ||
2132                             (document && document.body)) {
2133
2134                             var scope = el;
2135                             if (item.override) {
2136                                 if (item.override === true) {
2137                                     scope = item.obj;
2138                                 } else {
2139                                     scope = item.override;
2140                                 }
2141                             }
2142                             item.fn.call(scope, item.obj);
2143                             onAvailStack[i] = null;
2144                         }
2145                     } else {
2146                         notAvail.push(item);
2147                     }
2148                 }
2149             }
2150
2151             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2152
2153             if (tryAgain) {
2154
2155                 this.startInterval();
2156             } else {
2157                 clearInterval(this._interval);
2158                 this._interval = null;
2159             }
2160
2161             this.locked = false;
2162
2163             return true;
2164
2165         },
2166
2167
2168         purgeElement: function(el, recurse, eventName) {
2169             var elListeners = this.getListeners(el, eventName);
2170             if (elListeners) {
2171                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2172                     var l = elListeners[i];
2173                     this.removeListener(el, l.type, l.fn);
2174                 }
2175             }
2176
2177             if (recurse && el && el.childNodes) {
2178                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2179                     this.purgeElement(el.childNodes[i], recurse, eventName);
2180                 }
2181             }
2182         },
2183
2184
2185         getListeners: function(el, eventName) {
2186             var results = [], searchLists;
2187             if (!eventName) {
2188                 searchLists = [listeners, unloadListeners];
2189             } else if (eventName == "unload") {
2190                 searchLists = [unloadListeners];
2191             } else {
2192                 searchLists = [listeners];
2193             }
2194
2195             for (var j = 0; j < searchLists.length; ++j) {
2196                 var searchList = searchLists[j];
2197                 if (searchList && searchList.length > 0) {
2198                     for (var i = 0,len = searchList.length; i < len; ++i) {
2199                         var l = searchList[i];
2200                         if (l && l[this.EL] === el &&
2201                             (!eventName || eventName === l[this.TYPE])) {
2202                             results.push({
2203                                 type:   l[this.TYPE],
2204                                 fn:     l[this.FN],
2205                                 obj:    l[this.OBJ],
2206                                 adjust: l[this.ADJ_SCOPE],
2207                                 index:  i
2208                             });
2209                         }
2210                     }
2211                 }
2212             }
2213
2214             return (results.length) ? results : null;
2215         },
2216
2217
2218         _unload: function(e) {
2219
2220             var EU = Roo.lib.Event, i, j, l, len, index;
2221
2222             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2223                 l = unloadListeners[i];
2224                 if (l) {
2225                     var scope = window;
2226                     if (l[EU.ADJ_SCOPE]) {
2227                         if (l[EU.ADJ_SCOPE] === true) {
2228                             scope = l[EU.OBJ];
2229                         } else {
2230                             scope = l[EU.ADJ_SCOPE];
2231                         }
2232                     }
2233                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2234                     unloadListeners[i] = null;
2235                     l = null;
2236                     scope = null;
2237                 }
2238             }
2239
2240             unloadListeners = null;
2241
2242             if (listeners && listeners.length > 0) {
2243                 j = listeners.length;
2244                 while (j) {
2245                     index = j - 1;
2246                     l = listeners[index];
2247                     if (l) {
2248                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2249                                 l[EU.FN], index);
2250                     }
2251                     j = j - 1;
2252                 }
2253                 l = null;
2254
2255                 EU.clearCache();
2256             }
2257
2258             EU.doRemove(window, "unload", EU._unload);
2259
2260         },
2261
2262
2263         getScroll: function() {
2264             var dd = document.documentElement, db = document.body;
2265             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2266                 return [dd.scrollTop, dd.scrollLeft];
2267             } else if (db) {
2268                 return [db.scrollTop, db.scrollLeft];
2269             } else {
2270                 return [0, 0];
2271             }
2272         },
2273
2274
2275         doAdd: function () {
2276             if (window.addEventListener) {
2277                 return function(el, eventName, fn, capture) {
2278                     el.addEventListener(eventName, fn, (capture));
2279                 };
2280             } else if (window.attachEvent) {
2281                 return function(el, eventName, fn, capture) {
2282                     el.attachEvent("on" + eventName, fn);
2283                 };
2284             } else {
2285                 return function() {
2286                 };
2287             }
2288         }(),
2289
2290
2291         doRemove: function() {
2292             if (window.removeEventListener) {
2293                 return function (el, eventName, fn, capture) {
2294                     el.removeEventListener(eventName, fn, (capture));
2295                 };
2296             } else if (window.detachEvent) {
2297                 return function (el, eventName, fn) {
2298                     el.detachEvent("on" + eventName, fn);
2299                 };
2300             } else {
2301                 return function() {
2302                 };
2303             }
2304         }()
2305     };
2306     
2307 }();
2308 (function() {     
2309    
2310     var E = Roo.lib.Event;
2311     E.on = E.addListener;
2312     E.un = E.removeListener;
2313
2314     if (document && document.body) {
2315         E._load();
2316     } else {
2317         E.doAdd(window, "load", E._load);
2318     }
2319     E.doAdd(window, "unload", E._unload);
2320     E._tryPreloadAttach();
2321 })();
2322
2323 /*
2324  * Portions of this file are based on pieces of Yahoo User Interface Library
2325  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2326  * YUI licensed under the BSD License:
2327  * http://developer.yahoo.net/yui/license.txt
2328  * <script type="text/javascript">
2329  *
2330  */
2331
2332 (function() {
2333     
2334     Roo.lib.Ajax = {
2335         request : function(method, uri, cb, data, options) {
2336             if(options){
2337                 var hs = options.headers;
2338                 if(hs){
2339                     for(var h in hs){
2340                         if(hs.hasOwnProperty(h)){
2341                             this.initHeader(h, hs[h], false);
2342                         }
2343                     }
2344                 }
2345                 if(options.xmlData){
2346                     this.initHeader('Content-Type', 'text/xml', false);
2347                     method = 'POST';
2348                     data = options.xmlData;
2349                 }
2350             }
2351
2352             return this.asyncRequest(method, uri, cb, data);
2353         },
2354
2355         serializeForm : function(form) {
2356             if(typeof form == 'string') {
2357                 form = (document.getElementById(form) || document.forms[form]);
2358             }
2359
2360             var el, name, val, disabled, data = '', hasSubmit = false;
2361             for (var i = 0; i < form.elements.length; i++) {
2362                 el = form.elements[i];
2363                 disabled = form.elements[i].disabled;
2364                 name = form.elements[i].name;
2365                 val = form.elements[i].value;
2366
2367                 if (!disabled && name){
2368                     switch (el.type)
2369                             {
2370                         case 'select-one':
2371                         case 'select-multiple':
2372                             for (var j = 0; j < el.options.length; j++) {
2373                                 if (el.options[j].selected) {
2374                                     if (Roo.isIE) {
2375                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2376                                     }
2377                                     else {
2378                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2379                                     }
2380                                 }
2381                             }
2382                             break;
2383                         case 'radio':
2384                         case 'checkbox':
2385                             if (el.checked) {
2386                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2387                             }
2388                             break;
2389                         case 'file':
2390
2391                         case undefined:
2392
2393                         case 'reset':
2394
2395                         case 'button':
2396
2397                             break;
2398                         case 'submit':
2399                             if(hasSubmit == false) {
2400                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2401                                 hasSubmit = true;
2402                             }
2403                             break;
2404                         default:
2405                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2406                             break;
2407                     }
2408                 }
2409             }
2410             data = data.substr(0, data.length - 1);
2411             return data;
2412         },
2413
2414         headers:{},
2415
2416         hasHeaders:false,
2417
2418         useDefaultHeader:true,
2419
2420         defaultPostHeader:'application/x-www-form-urlencoded',
2421
2422         useDefaultXhrHeader:true,
2423
2424         defaultXhrHeader:'XMLHttpRequest',
2425
2426         hasDefaultHeaders:true,
2427
2428         defaultHeaders:{},
2429
2430         poll:{},
2431
2432         timeout:{},
2433
2434         pollInterval:50,
2435
2436         transactionId:0,
2437
2438         setProgId:function(id)
2439         {
2440             this.activeX.unshift(id);
2441         },
2442
2443         setDefaultPostHeader:function(b)
2444         {
2445             this.useDefaultHeader = b;
2446         },
2447
2448         setDefaultXhrHeader:function(b)
2449         {
2450             this.useDefaultXhrHeader = b;
2451         },
2452
2453         setPollingInterval:function(i)
2454         {
2455             if (typeof i == 'number' && isFinite(i)) {
2456                 this.pollInterval = i;
2457             }
2458         },
2459
2460         createXhrObject:function(transactionId)
2461         {
2462             var obj,http;
2463             try
2464             {
2465
2466                 http = new XMLHttpRequest();
2467
2468                 obj = { conn:http, tId:transactionId };
2469             }
2470             catch(e)
2471             {
2472                 for (var i = 0; i < this.activeX.length; ++i) {
2473                     try
2474                     {
2475
2476                         http = new ActiveXObject(this.activeX[i]);
2477
2478                         obj = { conn:http, tId:transactionId };
2479                         break;
2480                     }
2481                     catch(e) {
2482                     }
2483                 }
2484             }
2485             finally
2486             {
2487                 return obj;
2488             }
2489         },
2490
2491         getConnectionObject:function()
2492         {
2493             var o;
2494             var tId = this.transactionId;
2495
2496             try
2497             {
2498                 o = this.createXhrObject(tId);
2499                 if (o) {
2500                     this.transactionId++;
2501                 }
2502             }
2503             catch(e) {
2504             }
2505             finally
2506             {
2507                 return o;
2508             }
2509         },
2510
2511         asyncRequest:function(method, uri, callback, postData)
2512         {
2513             var o = this.getConnectionObject();
2514
2515             if (!o) {
2516                 return null;
2517             }
2518             else {
2519                 o.conn.open(method, uri, true);
2520
2521                 if (this.useDefaultXhrHeader) {
2522                     if (!this.defaultHeaders['X-Requested-With']) {
2523                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2524                     }
2525                 }
2526
2527                 if(postData && this.useDefaultHeader){
2528                     this.initHeader('Content-Type', this.defaultPostHeader);
2529                 }
2530
2531                  if (this.hasDefaultHeaders || this.hasHeaders) {
2532                     this.setHeader(o);
2533                 }
2534
2535                 this.handleReadyState(o, callback);
2536                 o.conn.send(postData || null);
2537
2538                 return o;
2539             }
2540         },
2541
2542         handleReadyState:function(o, callback)
2543         {
2544             var oConn = this;
2545
2546             if (callback && callback.timeout) {
2547                 this.timeout[o.tId] = window.setTimeout(function() {
2548                     oConn.abort(o, callback, true);
2549                 }, callback.timeout);
2550             }
2551
2552             this.poll[o.tId] = window.setInterval(
2553                     function() {
2554                         if (o.conn && o.conn.readyState == 4) {
2555                             window.clearInterval(oConn.poll[o.tId]);
2556                             delete oConn.poll[o.tId];
2557
2558                             if(callback && callback.timeout) {
2559                                 window.clearTimeout(oConn.timeout[o.tId]);
2560                                 delete oConn.timeout[o.tId];
2561                             }
2562
2563                             oConn.handleTransactionResponse(o, callback);
2564                         }
2565                     }
2566                     , this.pollInterval);
2567         },
2568
2569         handleTransactionResponse:function(o, callback, isAbort)
2570         {
2571
2572             if (!callback) {
2573                 this.releaseObject(o);
2574                 return;
2575             }
2576
2577             var httpStatus, responseObject;
2578
2579             try
2580             {
2581                 if (o.conn.status !== undefined && o.conn.status != 0) {
2582                     httpStatus = o.conn.status;
2583                 }
2584                 else {
2585                     httpStatus = 13030;
2586                 }
2587             }
2588             catch(e) {
2589
2590
2591                 httpStatus = 13030;
2592             }
2593
2594             if (httpStatus >= 200 && httpStatus < 300) {
2595                 responseObject = this.createResponseObject(o, callback.argument);
2596                 if (callback.success) {
2597                     if (!callback.scope) {
2598                         callback.success(responseObject);
2599                     }
2600                     else {
2601
2602
2603                         callback.success.apply(callback.scope, [responseObject]);
2604                     }
2605                 }
2606             }
2607             else {
2608                 switch (httpStatus) {
2609
2610                     case 12002:
2611                     case 12029:
2612                     case 12030:
2613                     case 12031:
2614                     case 12152:
2615                     case 13030:
2616                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2617                         if (callback.failure) {
2618                             if (!callback.scope) {
2619                                 callback.failure(responseObject);
2620                             }
2621                             else {
2622                                 callback.failure.apply(callback.scope, [responseObject]);
2623                             }
2624                         }
2625                         break;
2626                     default:
2627                         responseObject = this.createResponseObject(o, callback.argument);
2628                         if (callback.failure) {
2629                             if (!callback.scope) {
2630                                 callback.failure(responseObject);
2631                             }
2632                             else {
2633                                 callback.failure.apply(callback.scope, [responseObject]);
2634                             }
2635                         }
2636                 }
2637             }
2638
2639             this.releaseObject(o);
2640             responseObject = null;
2641         },
2642
2643         createResponseObject:function(o, callbackArg)
2644         {
2645             var obj = {};
2646             var headerObj = {};
2647
2648             try
2649             {
2650                 var headerStr = o.conn.getAllResponseHeaders();
2651                 var header = headerStr.split('\n');
2652                 for (var i = 0; i < header.length; i++) {
2653                     var delimitPos = header[i].indexOf(':');
2654                     if (delimitPos != -1) {
2655                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2656                     }
2657                 }
2658             }
2659             catch(e) {
2660             }
2661
2662             obj.tId = o.tId;
2663             obj.status = o.conn.status;
2664             obj.statusText = o.conn.statusText;
2665             obj.getResponseHeader = headerObj;
2666             obj.getAllResponseHeaders = headerStr;
2667             obj.responseText = o.conn.responseText;
2668             obj.responseXML = o.conn.responseXML;
2669
2670             if (typeof callbackArg !== undefined) {
2671                 obj.argument = callbackArg;
2672             }
2673
2674             return obj;
2675         },
2676
2677         createExceptionObject:function(tId, callbackArg, isAbort)
2678         {
2679             var COMM_CODE = 0;
2680             var COMM_ERROR = 'communication failure';
2681             var ABORT_CODE = -1;
2682             var ABORT_ERROR = 'transaction aborted';
2683
2684             var obj = {};
2685
2686             obj.tId = tId;
2687             if (isAbort) {
2688                 obj.status = ABORT_CODE;
2689                 obj.statusText = ABORT_ERROR;
2690             }
2691             else {
2692                 obj.status = COMM_CODE;
2693                 obj.statusText = COMM_ERROR;
2694             }
2695
2696             if (callbackArg) {
2697                 obj.argument = callbackArg;
2698             }
2699
2700             return obj;
2701         },
2702
2703         initHeader:function(label, value, isDefault)
2704         {
2705             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2706
2707             if (headerObj[label] === undefined) {
2708                 headerObj[label] = value;
2709             }
2710             else {
2711
2712
2713                 headerObj[label] = value + "," + headerObj[label];
2714             }
2715
2716             if (isDefault) {
2717                 this.hasDefaultHeaders = true;
2718             }
2719             else {
2720                 this.hasHeaders = true;
2721             }
2722         },
2723
2724
2725         setHeader:function(o)
2726         {
2727             if (this.hasDefaultHeaders) {
2728                 for (var prop in this.defaultHeaders) {
2729                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2730                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2731                     }
2732                 }
2733             }
2734
2735             if (this.hasHeaders) {
2736                 for (var prop in this.headers) {
2737                     if (this.headers.hasOwnProperty(prop)) {
2738                         o.conn.setRequestHeader(prop, this.headers[prop]);
2739                     }
2740                 }
2741                 this.headers = {};
2742                 this.hasHeaders = false;
2743             }
2744         },
2745
2746         resetDefaultHeaders:function() {
2747             delete this.defaultHeaders;
2748             this.defaultHeaders = {};
2749             this.hasDefaultHeaders = false;
2750         },
2751
2752         abort:function(o, callback, isTimeout)
2753         {
2754             if(this.isCallInProgress(o)) {
2755                 o.conn.abort();
2756                 window.clearInterval(this.poll[o.tId]);
2757                 delete this.poll[o.tId];
2758                 if (isTimeout) {
2759                     delete this.timeout[o.tId];
2760                 }
2761
2762                 this.handleTransactionResponse(o, callback, true);
2763
2764                 return true;
2765             }
2766             else {
2767                 return false;
2768             }
2769         },
2770
2771
2772         isCallInProgress:function(o)
2773         {
2774             if (o && o.conn) {
2775                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2776             }
2777             else {
2778
2779                 return false;
2780             }
2781         },
2782
2783
2784         releaseObject:function(o)
2785         {
2786
2787             o.conn = null;
2788
2789             o = null;
2790         },
2791
2792         activeX:[
2793         'MSXML2.XMLHTTP.3.0',
2794         'MSXML2.XMLHTTP',
2795         'Microsoft.XMLHTTP'
2796         ]
2797
2798
2799     };
2800 })();/*
2801  * Portions of this file are based on pieces of Yahoo User Interface Library
2802  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2803  * YUI licensed under the BSD License:
2804  * http://developer.yahoo.net/yui/license.txt
2805  * <script type="text/javascript">
2806  *
2807  */
2808
2809 Roo.lib.Region = function(t, r, b, l) {
2810     this.top = t;
2811     this[1] = t;
2812     this.right = r;
2813     this.bottom = b;
2814     this.left = l;
2815     this[0] = l;
2816 };
2817
2818
2819 Roo.lib.Region.prototype = {
2820     contains : function(region) {
2821         return ( region.left >= this.left &&
2822                  region.right <= this.right &&
2823                  region.top >= this.top &&
2824                  region.bottom <= this.bottom    );
2825
2826     },
2827
2828     getArea : function() {
2829         return ( (this.bottom - this.top) * (this.right - this.left) );
2830     },
2831
2832     intersect : function(region) {
2833         var t = Math.max(this.top, region.top);
2834         var r = Math.min(this.right, region.right);
2835         var b = Math.min(this.bottom, region.bottom);
2836         var l = Math.max(this.left, region.left);
2837
2838         if (b >= t && r >= l) {
2839             return new Roo.lib.Region(t, r, b, l);
2840         } else {
2841             return null;
2842         }
2843     },
2844     union : function(region) {
2845         var t = Math.min(this.top, region.top);
2846         var r = Math.max(this.right, region.right);
2847         var b = Math.max(this.bottom, region.bottom);
2848         var l = Math.min(this.left, region.left);
2849
2850         return new Roo.lib.Region(t, r, b, l);
2851     },
2852
2853     adjust : function(t, l, b, r) {
2854         this.top += t;
2855         this.left += l;
2856         this.right += r;
2857         this.bottom += b;
2858         return this;
2859     }
2860 };
2861
2862 Roo.lib.Region.getRegion = function(el) {
2863     var p = Roo.lib.Dom.getXY(el);
2864
2865     var t = p[1];
2866     var r = p[0] + el.offsetWidth;
2867     var b = p[1] + el.offsetHeight;
2868     var l = p[0];
2869
2870     return new Roo.lib.Region(t, r, b, l);
2871 };
2872 /*
2873  * Portions of this file are based on pieces of Yahoo User Interface Library
2874  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2875  * YUI licensed under the BSD License:
2876  * http://developer.yahoo.net/yui/license.txt
2877  * <script type="text/javascript">
2878  *
2879  */
2880 //@@dep Roo.lib.Region
2881
2882
2883 Roo.lib.Point = function(x, y) {
2884     if (x instanceof Array) {
2885         y = x[1];
2886         x = x[0];
2887     }
2888     this.x = this.right = this.left = this[0] = x;
2889     this.y = this.top = this.bottom = this[1] = y;
2890 };
2891
2892 Roo.lib.Point.prototype = new Roo.lib.Region();
2893 /*
2894  * Portions of this file are based on pieces of Yahoo User Interface Library
2895  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2896  * YUI licensed under the BSD License:
2897  * http://developer.yahoo.net/yui/license.txt
2898  * <script type="text/javascript">
2899  *
2900  */
2901  
2902 (function() {   
2903
2904     Roo.lib.Anim = {
2905         scroll : function(el, args, duration, easing, cb, scope) {
2906             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2907         },
2908
2909         motion : function(el, args, duration, easing, cb, scope) {
2910             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2911         },
2912
2913         color : function(el, args, duration, easing, cb, scope) {
2914             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2915         },
2916
2917         run : function(el, args, duration, easing, cb, scope, type) {
2918             type = type || Roo.lib.AnimBase;
2919             if (typeof easing == "string") {
2920                 easing = Roo.lib.Easing[easing];
2921             }
2922             var anim = new type(el, args, duration, easing);
2923             anim.animateX(function() {
2924                 Roo.callback(cb, scope);
2925             });
2926             return anim;
2927         }
2928     };
2929 })();/*
2930  * Portions of this file are based on pieces of Yahoo User Interface Library
2931  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2932  * YUI licensed under the BSD License:
2933  * http://developer.yahoo.net/yui/license.txt
2934  * <script type="text/javascript">
2935  *
2936  */
2937
2938 (function() {    
2939     var libFlyweight;
2940     
2941     function fly(el) {
2942         if (!libFlyweight) {
2943             libFlyweight = new Roo.Element.Flyweight();
2944         }
2945         libFlyweight.dom = el;
2946         return libFlyweight;
2947     }
2948
2949     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2950     
2951    
2952     
2953     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2954         if (el) {
2955             this.init(el, attributes, duration, method);
2956         }
2957     };
2958
2959     Roo.lib.AnimBase.fly = fly;
2960     
2961     
2962     
2963     Roo.lib.AnimBase.prototype = {
2964
2965         toString: function() {
2966             var el = this.getEl();
2967             var id = el.id || el.tagName;
2968             return ("Anim " + id);
2969         },
2970
2971         patterns: {
2972             noNegatives:        /width|height|opacity|padding/i,
2973             offsetAttribute:  /^((width|height)|(top|left))$/,
2974             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
2975             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
2976         },
2977
2978
2979         doMethod: function(attr, start, end) {
2980             return this.method(this.currentFrame, start, end - start, this.totalFrames);
2981         },
2982
2983
2984         setAttribute: function(attr, val, unit) {
2985             if (this.patterns.noNegatives.test(attr)) {
2986                 val = (val > 0) ? val : 0;
2987             }
2988
2989             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
2990         },
2991
2992
2993         getAttribute: function(attr) {
2994             var el = this.getEl();
2995             var val = fly(el).getStyle(attr);
2996
2997             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
2998                 return parseFloat(val);
2999             }
3000
3001             var a = this.patterns.offsetAttribute.exec(attr) || [];
3002             var pos = !!( a[3] );
3003             var box = !!( a[2] );
3004
3005
3006             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3007                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3008             } else {
3009                 val = 0;
3010             }
3011
3012             return val;
3013         },
3014
3015
3016         getDefaultUnit: function(attr) {
3017             if (this.patterns.defaultUnit.test(attr)) {
3018                 return 'px';
3019             }
3020
3021             return '';
3022         },
3023
3024         animateX : function(callback, scope) {
3025             var f = function() {
3026                 this.onComplete.removeListener(f);
3027                 if (typeof callback == "function") {
3028                     callback.call(scope || this, this);
3029                 }
3030             };
3031             this.onComplete.addListener(f, this);
3032             this.animate();
3033         },
3034
3035
3036         setRuntimeAttribute: function(attr) {
3037             var start;
3038             var end;
3039             var attributes = this.attributes;
3040
3041             this.runtimeAttributes[attr] = {};
3042
3043             var isset = function(prop) {
3044                 return (typeof prop !== 'undefined');
3045             };
3046
3047             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3048                 return false;
3049             }
3050
3051             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3052
3053
3054             if (isset(attributes[attr]['to'])) {
3055                 end = attributes[attr]['to'];
3056             } else if (isset(attributes[attr]['by'])) {
3057                 if (start.constructor == Array) {
3058                     end = [];
3059                     for (var i = 0, len = start.length; i < len; ++i) {
3060                         end[i] = start[i] + attributes[attr]['by'][i];
3061                     }
3062                 } else {
3063                     end = start + attributes[attr]['by'];
3064                 }
3065             }
3066
3067             this.runtimeAttributes[attr].start = start;
3068             this.runtimeAttributes[attr].end = end;
3069
3070
3071             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3072         },
3073
3074
3075         init: function(el, attributes, duration, method) {
3076
3077             var isAnimated = false;
3078
3079
3080             var startTime = null;
3081
3082
3083             var actualFrames = 0;
3084
3085
3086             el = Roo.getDom(el);
3087
3088
3089             this.attributes = attributes || {};
3090
3091
3092             this.duration = duration || 1;
3093
3094
3095             this.method = method || Roo.lib.Easing.easeNone;
3096
3097
3098             this.useSeconds = true;
3099
3100
3101             this.currentFrame = 0;
3102
3103
3104             this.totalFrames = Roo.lib.AnimMgr.fps;
3105
3106
3107             this.getEl = function() {
3108                 return el;
3109             };
3110
3111
3112             this.isAnimated = function() {
3113                 return isAnimated;
3114             };
3115
3116
3117             this.getStartTime = function() {
3118                 return startTime;
3119             };
3120
3121             this.runtimeAttributes = {};
3122
3123
3124             this.animate = function() {
3125                 if (this.isAnimated()) {
3126                     return false;
3127                 }
3128
3129                 this.currentFrame = 0;
3130
3131                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3132
3133                 Roo.lib.AnimMgr.registerElement(this);
3134             };
3135
3136
3137             this.stop = function(finish) {
3138                 if (finish) {
3139                     this.currentFrame = this.totalFrames;
3140                     this._onTween.fire();
3141                 }
3142                 Roo.lib.AnimMgr.stop(this);
3143             };
3144
3145             var onStart = function() {
3146                 this.onStart.fire();
3147
3148                 this.runtimeAttributes = {};
3149                 for (var attr in this.attributes) {
3150                     this.setRuntimeAttribute(attr);
3151                 }
3152
3153                 isAnimated = true;
3154                 actualFrames = 0;
3155                 startTime = new Date();
3156             };
3157
3158
3159             var onTween = function() {
3160                 var data = {
3161                     duration: new Date() - this.getStartTime(),
3162                     currentFrame: this.currentFrame
3163                 };
3164
3165                 data.toString = function() {
3166                     return (
3167                             'duration: ' + data.duration +
3168                             ', currentFrame: ' + data.currentFrame
3169                             );
3170                 };
3171
3172                 this.onTween.fire(data);
3173
3174                 var runtimeAttributes = this.runtimeAttributes;
3175
3176                 for (var attr in runtimeAttributes) {
3177                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3178                 }
3179
3180                 actualFrames += 1;
3181             };
3182
3183             var onComplete = function() {
3184                 var actual_duration = (new Date() - startTime) / 1000 ;
3185
3186                 var data = {
3187                     duration: actual_duration,
3188                     frames: actualFrames,
3189                     fps: actualFrames / actual_duration
3190                 };
3191
3192                 data.toString = function() {
3193                     return (
3194                             'duration: ' + data.duration +
3195                             ', frames: ' + data.frames +
3196                             ', fps: ' + data.fps
3197                             );
3198                 };
3199
3200                 isAnimated = false;
3201                 actualFrames = 0;
3202                 this.onComplete.fire(data);
3203             };
3204
3205
3206             this._onStart = new Roo.util.Event(this);
3207             this.onStart = new Roo.util.Event(this);
3208             this.onTween = new Roo.util.Event(this);
3209             this._onTween = new Roo.util.Event(this);
3210             this.onComplete = new Roo.util.Event(this);
3211             this._onComplete = new Roo.util.Event(this);
3212             this._onStart.addListener(onStart);
3213             this._onTween.addListener(onTween);
3214             this._onComplete.addListener(onComplete);
3215         }
3216     };
3217 })();
3218 /*
3219  * Portions of this file are based on pieces of Yahoo User Interface Library
3220  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3221  * YUI licensed under the BSD License:
3222  * http://developer.yahoo.net/yui/license.txt
3223  * <script type="text/javascript">
3224  *
3225  */
3226
3227 Roo.lib.AnimMgr = new function() {
3228
3229         var thread = null;
3230
3231
3232         var queue = [];
3233
3234
3235         var tweenCount = 0;
3236
3237
3238         this.fps = 1000;
3239
3240
3241         this.delay = 1;
3242
3243
3244         this.registerElement = function(tween) {
3245             queue[queue.length] = tween;
3246             tweenCount += 1;
3247             tween._onStart.fire();
3248             this.start();
3249         };
3250
3251
3252         this.unRegister = function(tween, index) {
3253             tween._onComplete.fire();
3254             index = index || getIndex(tween);
3255             if (index != -1) {
3256                 queue.splice(index, 1);
3257             }
3258
3259             tweenCount -= 1;
3260             if (tweenCount <= 0) {
3261                 this.stop();
3262             }
3263         };
3264
3265
3266         this.start = function() {
3267             if (thread === null) {
3268                 thread = setInterval(this.run, this.delay);
3269             }
3270         };
3271
3272
3273         this.stop = function(tween) {
3274             if (!tween) {
3275                 clearInterval(thread);
3276
3277                 for (var i = 0, len = queue.length; i < len; ++i) {
3278                     if (queue[0].isAnimated()) {
3279                         this.unRegister(queue[0], 0);
3280                     }
3281                 }
3282
3283                 queue = [];
3284                 thread = null;
3285                 tweenCount = 0;
3286             }
3287             else {
3288                 this.unRegister(tween);
3289             }
3290         };
3291
3292
3293         this.run = function() {
3294             for (var i = 0, len = queue.length; i < len; ++i) {
3295                 var tween = queue[i];
3296                 if (!tween || !tween.isAnimated()) {
3297                     continue;
3298                 }
3299
3300                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3301                 {
3302                     tween.currentFrame += 1;
3303
3304                     if (tween.useSeconds) {
3305                         correctFrame(tween);
3306                     }
3307                     tween._onTween.fire();
3308                 }
3309                 else {
3310                     Roo.lib.AnimMgr.stop(tween, i);
3311                 }
3312             }
3313         };
3314
3315         var getIndex = function(anim) {
3316             for (var i = 0, len = queue.length; i < len; ++i) {
3317                 if (queue[i] == anim) {
3318                     return i;
3319                 }
3320             }
3321             return -1;
3322         };
3323
3324
3325         var correctFrame = function(tween) {
3326             var frames = tween.totalFrames;
3327             var frame = tween.currentFrame;
3328             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3329             var elapsed = (new Date() - tween.getStartTime());
3330             var tweak = 0;
3331
3332             if (elapsed < tween.duration * 1000) {
3333                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3334             } else {
3335                 tweak = frames - (frame + 1);
3336             }
3337             if (tweak > 0 && isFinite(tweak)) {
3338                 if (tween.currentFrame + tweak >= frames) {
3339                     tweak = frames - (frame + 1);
3340                 }
3341
3342                 tween.currentFrame += tweak;
3343             }
3344         };
3345     };/*
3346  * Portions of this file are based on pieces of Yahoo User Interface Library
3347  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3348  * YUI licensed under the BSD License:
3349  * http://developer.yahoo.net/yui/license.txt
3350  * <script type="text/javascript">
3351  *
3352  */
3353 Roo.lib.Bezier = new function() {
3354
3355         this.getPosition = function(points, t) {
3356             var n = points.length;
3357             var tmp = [];
3358
3359             for (var i = 0; i < n; ++i) {
3360                 tmp[i] = [points[i][0], points[i][1]];
3361             }
3362
3363             for (var j = 1; j < n; ++j) {
3364                 for (i = 0; i < n - j; ++i) {
3365                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3366                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3367                 }
3368             }
3369
3370             return [ tmp[0][0], tmp[0][1] ];
3371
3372         };
3373     };/*
3374  * Portions of this file are based on pieces of Yahoo User Interface Library
3375  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3376  * YUI licensed under the BSD License:
3377  * http://developer.yahoo.net/yui/license.txt
3378  * <script type="text/javascript">
3379  *
3380  */
3381 (function() {
3382
3383     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3384         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3385     };
3386
3387     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3388
3389     var fly = Roo.lib.AnimBase.fly;
3390     var Y = Roo.lib;
3391     var superclass = Y.ColorAnim.superclass;
3392     var proto = Y.ColorAnim.prototype;
3393
3394     proto.toString = function() {
3395         var el = this.getEl();
3396         var id = el.id || el.tagName;
3397         return ("ColorAnim " + id);
3398     };
3399
3400     proto.patterns.color = /color$/i;
3401     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3402     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3403     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3404     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3405
3406
3407     proto.parseColor = function(s) {
3408         if (s.length == 3) {
3409             return s;
3410         }
3411
3412         var c = this.patterns.hex.exec(s);
3413         if (c && c.length == 4) {
3414             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3415         }
3416
3417         c = this.patterns.rgb.exec(s);
3418         if (c && c.length == 4) {
3419             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3420         }
3421
3422         c = this.patterns.hex3.exec(s);
3423         if (c && c.length == 4) {
3424             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3425         }
3426
3427         return null;
3428     };
3429     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3430     proto.getAttribute = function(attr) {
3431         var el = this.getEl();
3432         if (this.patterns.color.test(attr)) {
3433             var val = fly(el).getStyle(attr);
3434
3435             if (this.patterns.transparent.test(val)) {
3436                 var parent = el.parentNode;
3437                 val = fly(parent).getStyle(attr);
3438
3439                 while (parent && this.patterns.transparent.test(val)) {
3440                     parent = parent.parentNode;
3441                     val = fly(parent).getStyle(attr);
3442                     if (parent.tagName.toUpperCase() == 'HTML') {
3443                         val = '#fff';
3444                     }
3445                 }
3446             }
3447         } else {
3448             val = superclass.getAttribute.call(this, attr);
3449         }
3450
3451         return val;
3452     };
3453     proto.getAttribute = function(attr) {
3454         var el = this.getEl();
3455         if (this.patterns.color.test(attr)) {
3456             var val = fly(el).getStyle(attr);
3457
3458             if (this.patterns.transparent.test(val)) {
3459                 var parent = el.parentNode;
3460                 val = fly(parent).getStyle(attr);
3461
3462                 while (parent && this.patterns.transparent.test(val)) {
3463                     parent = parent.parentNode;
3464                     val = fly(parent).getStyle(attr);
3465                     if (parent.tagName.toUpperCase() == 'HTML') {
3466                         val = '#fff';
3467                     }
3468                 }
3469             }
3470         } else {
3471             val = superclass.getAttribute.call(this, attr);
3472         }
3473
3474         return val;
3475     };
3476
3477     proto.doMethod = function(attr, start, end) {
3478         var val;
3479
3480         if (this.patterns.color.test(attr)) {
3481             val = [];
3482             for (var i = 0, len = start.length; i < len; ++i) {
3483                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3484             }
3485
3486             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3487         }
3488         else {
3489             val = superclass.doMethod.call(this, attr, start, end);
3490         }
3491
3492         return val;
3493     };
3494
3495     proto.setRuntimeAttribute = function(attr) {
3496         superclass.setRuntimeAttribute.call(this, attr);
3497
3498         if (this.patterns.color.test(attr)) {
3499             var attributes = this.attributes;
3500             var start = this.parseColor(this.runtimeAttributes[attr].start);
3501             var end = this.parseColor(this.runtimeAttributes[attr].end);
3502
3503             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3504                 end = this.parseColor(attributes[attr].by);
3505
3506                 for (var i = 0, len = start.length; i < len; ++i) {
3507                     end[i] = start[i] + end[i];
3508                 }
3509             }
3510
3511             this.runtimeAttributes[attr].start = start;
3512             this.runtimeAttributes[attr].end = end;
3513         }
3514     };
3515 })();
3516
3517 /*
3518  * Portions of this file are based on pieces of Yahoo User Interface Library
3519  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3520  * YUI licensed under the BSD License:
3521  * http://developer.yahoo.net/yui/license.txt
3522  * <script type="text/javascript">
3523  *
3524  */
3525 Roo.lib.Easing = {
3526
3527
3528     easeNone: function (t, b, c, d) {
3529         return c * t / d + b;
3530     },
3531
3532
3533     easeIn: function (t, b, c, d) {
3534         return c * (t /= d) * t + b;
3535     },
3536
3537
3538     easeOut: function (t, b, c, d) {
3539         return -c * (t /= d) * (t - 2) + b;
3540     },
3541
3542
3543     easeBoth: function (t, b, c, d) {
3544         if ((t /= d / 2) < 1) {
3545             return c / 2 * t * t + b;
3546         }
3547
3548         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3549     },
3550
3551
3552     easeInStrong: function (t, b, c, d) {
3553         return c * (t /= d) * t * t * t + b;
3554     },
3555
3556
3557     easeOutStrong: function (t, b, c, d) {
3558         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3559     },
3560
3561
3562     easeBothStrong: function (t, b, c, d) {
3563         if ((t /= d / 2) < 1) {
3564             return c / 2 * t * t * t * t + b;
3565         }
3566
3567         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3568     },
3569
3570
3571
3572     elasticIn: function (t, b, c, d, a, p) {
3573         if (t == 0) {
3574             return b;
3575         }
3576         if ((t /= d) == 1) {
3577             return b + c;
3578         }
3579         if (!p) {
3580             p = d * .3;
3581         }
3582
3583         if (!a || a < Math.abs(c)) {
3584             a = c;
3585             var s = p / 4;
3586         }
3587         else {
3588             var s = p / (2 * Math.PI) * Math.asin(c / a);
3589         }
3590
3591         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3592     },
3593
3594
3595     elasticOut: function (t, b, c, d, a, p) {
3596         if (t == 0) {
3597             return b;
3598         }
3599         if ((t /= d) == 1) {
3600             return b + c;
3601         }
3602         if (!p) {
3603             p = d * .3;
3604         }
3605
3606         if (!a || a < Math.abs(c)) {
3607             a = c;
3608             var s = p / 4;
3609         }
3610         else {
3611             var s = p / (2 * Math.PI) * Math.asin(c / a);
3612         }
3613
3614         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3615     },
3616
3617
3618     elasticBoth: function (t, b, c, d, a, p) {
3619         if (t == 0) {
3620             return b;
3621         }
3622
3623         if ((t /= d / 2) == 2) {
3624             return b + c;
3625         }
3626
3627         if (!p) {
3628             p = d * (.3 * 1.5);
3629         }
3630
3631         if (!a || a < Math.abs(c)) {
3632             a = c;
3633             var s = p / 4;
3634         }
3635         else {
3636             var s = p / (2 * Math.PI) * Math.asin(c / a);
3637         }
3638
3639         if (t < 1) {
3640             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3641                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3642         }
3643         return a * Math.pow(2, -10 * (t -= 1)) *
3644                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3645     },
3646
3647
3648
3649     backIn: function (t, b, c, d, s) {
3650         if (typeof s == 'undefined') {
3651             s = 1.70158;
3652         }
3653         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3654     },
3655
3656
3657     backOut: function (t, b, c, d, s) {
3658         if (typeof s == 'undefined') {
3659             s = 1.70158;
3660         }
3661         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3662     },
3663
3664
3665     backBoth: function (t, b, c, d, s) {
3666         if (typeof s == 'undefined') {
3667             s = 1.70158;
3668         }
3669
3670         if ((t /= d / 2 ) < 1) {
3671             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3672         }
3673         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3674     },
3675
3676
3677     bounceIn: function (t, b, c, d) {
3678         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3679     },
3680
3681
3682     bounceOut: function (t, b, c, d) {
3683         if ((t /= d) < (1 / 2.75)) {
3684             return c * (7.5625 * t * t) + b;
3685         } else if (t < (2 / 2.75)) {
3686             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3687         } else if (t < (2.5 / 2.75)) {
3688             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3689         }
3690         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3691     },
3692
3693
3694     bounceBoth: function (t, b, c, d) {
3695         if (t < d / 2) {
3696             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3697         }
3698         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3699     }
3700 };/*
3701  * Portions of this file are based on pieces of Yahoo User Interface Library
3702  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3703  * YUI licensed under the BSD License:
3704  * http://developer.yahoo.net/yui/license.txt
3705  * <script type="text/javascript">
3706  *
3707  */
3708     (function() {
3709         Roo.lib.Motion = function(el, attributes, duration, method) {
3710             if (el) {
3711                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3712             }
3713         };
3714
3715         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3716
3717
3718         var Y = Roo.lib;
3719         var superclass = Y.Motion.superclass;
3720         var proto = Y.Motion.prototype;
3721
3722         proto.toString = function() {
3723             var el = this.getEl();
3724             var id = el.id || el.tagName;
3725             return ("Motion " + id);
3726         };
3727
3728         proto.patterns.points = /^points$/i;
3729
3730         proto.setAttribute = function(attr, val, unit) {
3731             if (this.patterns.points.test(attr)) {
3732                 unit = unit || 'px';
3733                 superclass.setAttribute.call(this, 'left', val[0], unit);
3734                 superclass.setAttribute.call(this, 'top', val[1], unit);
3735             } else {
3736                 superclass.setAttribute.call(this, attr, val, unit);
3737             }
3738         };
3739
3740         proto.getAttribute = function(attr) {
3741             if (this.patterns.points.test(attr)) {
3742                 var val = [
3743                         superclass.getAttribute.call(this, 'left'),
3744                         superclass.getAttribute.call(this, 'top')
3745                         ];
3746             } else {
3747                 val = superclass.getAttribute.call(this, attr);
3748             }
3749
3750             return val;
3751         };
3752
3753         proto.doMethod = function(attr, start, end) {
3754             var val = null;
3755
3756             if (this.patterns.points.test(attr)) {
3757                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3758                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3759             } else {
3760                 val = superclass.doMethod.call(this, attr, start, end);
3761             }
3762             return val;
3763         };
3764
3765         proto.setRuntimeAttribute = function(attr) {
3766             if (this.patterns.points.test(attr)) {
3767                 var el = this.getEl();
3768                 var attributes = this.attributes;
3769                 var start;
3770                 var control = attributes['points']['control'] || [];
3771                 var end;
3772                 var i, len;
3773
3774                 if (control.length > 0 && !(control[0] instanceof Array)) {
3775                     control = [control];
3776                 } else {
3777                     var tmp = [];
3778                     for (i = 0,len = control.length; i < len; ++i) {
3779                         tmp[i] = control[i];
3780                     }
3781                     control = tmp;
3782                 }
3783
3784                 Roo.fly(el).position();
3785
3786                 if (isset(attributes['points']['from'])) {
3787                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3788                 }
3789                 else {
3790                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3791                 }
3792
3793                 start = this.getAttribute('points');
3794
3795
3796                 if (isset(attributes['points']['to'])) {
3797                     end = translateValues.call(this, attributes['points']['to'], start);
3798
3799                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3800                     for (i = 0,len = control.length; i < len; ++i) {
3801                         control[i] = translateValues.call(this, control[i], start);
3802                     }
3803
3804
3805                 } else if (isset(attributes['points']['by'])) {
3806                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3807
3808                     for (i = 0,len = control.length; i < len; ++i) {
3809                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3810                     }
3811                 }
3812
3813                 this.runtimeAttributes[attr] = [start];
3814
3815                 if (control.length > 0) {
3816                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3817                 }
3818
3819                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3820             }
3821             else {
3822                 superclass.setRuntimeAttribute.call(this, attr);
3823             }
3824         };
3825
3826         var translateValues = function(val, start) {
3827             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3828             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3829
3830             return val;
3831         };
3832
3833         var isset = function(prop) {
3834             return (typeof prop !== 'undefined');
3835         };
3836     })();
3837 /*
3838  * Portions of this file are based on pieces of Yahoo User Interface Library
3839  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3840  * YUI licensed under the BSD License:
3841  * http://developer.yahoo.net/yui/license.txt
3842  * <script type="text/javascript">
3843  *
3844  */
3845     (function() {
3846         Roo.lib.Scroll = function(el, attributes, duration, method) {
3847             if (el) {
3848                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3849             }
3850         };
3851
3852         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3853
3854
3855         var Y = Roo.lib;
3856         var superclass = Y.Scroll.superclass;
3857         var proto = Y.Scroll.prototype;
3858
3859         proto.toString = function() {
3860             var el = this.getEl();
3861             var id = el.id || el.tagName;
3862             return ("Scroll " + id);
3863         };
3864
3865         proto.doMethod = function(attr, start, end) {
3866             var val = null;
3867
3868             if (attr == 'scroll') {
3869                 val = [
3870                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3871                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3872                         ];
3873
3874             } else {
3875                 val = superclass.doMethod.call(this, attr, start, end);
3876             }
3877             return val;
3878         };
3879
3880         proto.getAttribute = function(attr) {
3881             var val = null;
3882             var el = this.getEl();
3883
3884             if (attr == 'scroll') {
3885                 val = [ el.scrollLeft, el.scrollTop ];
3886             } else {
3887                 val = superclass.getAttribute.call(this, attr);
3888             }
3889
3890             return val;
3891         };
3892
3893         proto.setAttribute = function(attr, val, unit) {
3894             var el = this.getEl();
3895
3896             if (attr == 'scroll') {
3897                 el.scrollLeft = val[0];
3898                 el.scrollTop = val[1];
3899             } else {
3900                 superclass.setAttribute.call(this, attr, val, unit);
3901             }
3902         };
3903     })();
3904 /*
3905  * Based on:
3906  * Ext JS Library 1.1.1
3907  * Copyright(c) 2006-2007, Ext JS, LLC.
3908  *
3909  * Originally Released Under LGPL - original licence link has changed is not relivant.
3910  *
3911  * Fork - LGPL
3912  * <script type="text/javascript">
3913  */
3914  
3915
3916 /**
3917  * @class Roo.DomHelper
3918  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3919  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3920  * @singleton
3921  */
3922 Roo.DomHelper = function(){
3923     var tempTableEl = null;
3924     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3925     var tableRe = /^table|tbody|tr|td$/i;
3926     var xmlns = {};
3927     // build as innerHTML where available
3928     /** @ignore */
3929     var createHtml = function(o){
3930         if(typeof o == 'string'){
3931             return o;
3932         }
3933         var b = "";
3934         if(!o.tag){
3935             o.tag = "div";
3936         }
3937         b += "<" + o.tag;
3938         for(var attr in o){
3939             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3940             if(attr == "style"){
3941                 var s = o["style"];
3942                 if(typeof s == "function"){
3943                     s = s.call();
3944                 }
3945                 if(typeof s == "string"){
3946                     b += ' style="' + s + '"';
3947                 }else if(typeof s == "object"){
3948                     b += ' style="';
3949                     for(var key in s){
3950                         if(typeof s[key] != "function"){
3951                             b += key + ":" + s[key] + ";";
3952                         }
3953                     }
3954                     b += '"';
3955                 }
3956             }else{
3957                 if(attr == "cls"){
3958                     b += ' class="' + o["cls"] + '"';
3959                 }else if(attr == "htmlFor"){
3960                     b += ' for="' + o["htmlFor"] + '"';
3961                 }else{
3962                     b += " " + attr + '="' + o[attr] + '"';
3963                 }
3964             }
3965         }
3966         if(emptyTags.test(o.tag)){
3967             b += "/>";
3968         }else{
3969             b += ">";
3970             var cn = o.children || o.cn;
3971             if(cn){
3972                 //http://bugs.kde.org/show_bug.cgi?id=71506
3973                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3974                     for(var i = 0, len = cn.length; i < len; i++) {
3975                         b += createHtml(cn[i], b);
3976                     }
3977                 }else{
3978                     b += createHtml(cn, b);
3979                 }
3980             }
3981             if(o.html){
3982                 b += o.html;
3983             }
3984             b += "</" + o.tag + ">";
3985         }
3986         return b;
3987     };
3988
3989     // build as dom
3990     /** @ignore */
3991     var createDom = function(o, parentNode){
3992          
3993         // defininition craeted..
3994         var ns = false;
3995         if (o.ns && o.ns != 'html') {
3996                
3997             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
3998                 xmlns[o.ns] = o.xmlns;
3999                 ns = o.xmlns;
4000             }
4001             if (typeof(xmlns[o.ns]) == 'undefined') {
4002                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4003             }
4004             ns = xmlns[o.ns];
4005         }
4006         
4007         
4008         if (typeof(o) == 'string') {
4009             return parentNode.appendChild(document.createTextNode(o));
4010         }
4011         o.tag = o.tag || div;
4012         if (o.ns && Roo.isIE) {
4013             ns = false;
4014             o.tag = o.ns + ':' + o.tag;
4015             
4016         }
4017         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4018         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4019         for(var attr in o){
4020             
4021             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4022                     attr == "style" || typeof o[attr] == "function") continue;
4023                     
4024             if(attr=="cls" && Roo.isIE){
4025                 el.className = o["cls"];
4026             }else{
4027                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4028                 else el[attr] = o[attr];
4029             }
4030         }
4031         Roo.DomHelper.applyStyles(el, o.style);
4032         var cn = o.children || o.cn;
4033         if(cn){
4034             //http://bugs.kde.org/show_bug.cgi?id=71506
4035              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4036                 for(var i = 0, len = cn.length; i < len; i++) {
4037                     createDom(cn[i], el);
4038                 }
4039             }else{
4040                 createDom(cn, el);
4041             }
4042         }
4043         if(o.html){
4044             el.innerHTML = o.html;
4045         }
4046         if(parentNode){
4047            parentNode.appendChild(el);
4048         }
4049         return el;
4050     };
4051
4052     var ieTable = function(depth, s, h, e){
4053         tempTableEl.innerHTML = [s, h, e].join('');
4054         var i = -1, el = tempTableEl;
4055         while(++i < depth){
4056             el = el.firstChild;
4057         }
4058         return el;
4059     };
4060
4061     // kill repeat to save bytes
4062     var ts = '<table>',
4063         te = '</table>',
4064         tbs = ts+'<tbody>',
4065         tbe = '</tbody>'+te,
4066         trs = tbs + '<tr>',
4067         tre = '</tr>'+tbe;
4068
4069     /**
4070      * @ignore
4071      * Nasty code for IE's broken table implementation
4072      */
4073     var insertIntoTable = function(tag, where, el, html){
4074         if(!tempTableEl){
4075             tempTableEl = document.createElement('div');
4076         }
4077         var node;
4078         var before = null;
4079         if(tag == 'td'){
4080             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4081                 return;
4082             }
4083             if(where == 'beforebegin'){
4084                 before = el;
4085                 el = el.parentNode;
4086             } else{
4087                 before = el.nextSibling;
4088                 el = el.parentNode;
4089             }
4090             node = ieTable(4, trs, html, tre);
4091         }
4092         else if(tag == 'tr'){
4093             if(where == 'beforebegin'){
4094                 before = el;
4095                 el = el.parentNode;
4096                 node = ieTable(3, tbs, html, tbe);
4097             } else if(where == 'afterend'){
4098                 before = el.nextSibling;
4099                 el = el.parentNode;
4100                 node = ieTable(3, tbs, html, tbe);
4101             } else{ // INTO a TR
4102                 if(where == 'afterbegin'){
4103                     before = el.firstChild;
4104                 }
4105                 node = ieTable(4, trs, html, tre);
4106             }
4107         } else if(tag == 'tbody'){
4108             if(where == 'beforebegin'){
4109                 before = el;
4110                 el = el.parentNode;
4111                 node = ieTable(2, ts, html, te);
4112             } else if(where == 'afterend'){
4113                 before = el.nextSibling;
4114                 el = el.parentNode;
4115                 node = ieTable(2, ts, html, te);
4116             } else{
4117                 if(where == 'afterbegin'){
4118                     before = el.firstChild;
4119                 }
4120                 node = ieTable(3, tbs, html, tbe);
4121             }
4122         } else{ // TABLE
4123             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4124                 return;
4125             }
4126             if(where == 'afterbegin'){
4127                 before = el.firstChild;
4128             }
4129             node = ieTable(2, ts, html, te);
4130         }
4131         el.insertBefore(node, before);
4132         return node;
4133     };
4134
4135     return {
4136     /** True to force the use of DOM instead of html fragments @type Boolean */
4137     useDom : false,
4138
4139     /**
4140      * Returns the markup for the passed Element(s) config
4141      * @param {Object} o The Dom object spec (and children)
4142      * @return {String}
4143      */
4144     markup : function(o){
4145         return createHtml(o);
4146     },
4147
4148     /**
4149      * Applies a style specification to an element
4150      * @param {String/HTMLElement} el The element to apply styles to
4151      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4152      * a function which returns such a specification.
4153      */
4154     applyStyles : function(el, styles){
4155         if(styles){
4156            el = Roo.fly(el);
4157            if(typeof styles == "string"){
4158                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4159                var matches;
4160                while ((matches = re.exec(styles)) != null){
4161                    el.setStyle(matches[1], matches[2]);
4162                }
4163            }else if (typeof styles == "object"){
4164                for (var style in styles){
4165                   el.setStyle(style, styles[style]);
4166                }
4167            }else if (typeof styles == "function"){
4168                 Roo.DomHelper.applyStyles(el, styles.call());
4169            }
4170         }
4171     },
4172
4173     /**
4174      * Inserts an HTML fragment into the Dom
4175      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4176      * @param {HTMLElement} el The context element
4177      * @param {String} html The HTML fragmenet
4178      * @return {HTMLElement} The new node
4179      */
4180     insertHtml : function(where, el, html){
4181         where = where.toLowerCase();
4182         if(el.insertAdjacentHTML){
4183             if(tableRe.test(el.tagName)){
4184                 var rs;
4185                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4186                     return rs;
4187                 }
4188             }
4189             switch(where){
4190                 case "beforebegin":
4191                     el.insertAdjacentHTML('BeforeBegin', html);
4192                     return el.previousSibling;
4193                 case "afterbegin":
4194                     el.insertAdjacentHTML('AfterBegin', html);
4195                     return el.firstChild;
4196                 case "beforeend":
4197                     el.insertAdjacentHTML('BeforeEnd', html);
4198                     return el.lastChild;
4199                 case "afterend":
4200                     el.insertAdjacentHTML('AfterEnd', html);
4201                     return el.nextSibling;
4202             }
4203             throw 'Illegal insertion point -> "' + where + '"';
4204         }
4205         var range = el.ownerDocument.createRange();
4206         var frag;
4207         switch(where){
4208              case "beforebegin":
4209                 range.setStartBefore(el);
4210                 frag = range.createContextualFragment(html);
4211                 el.parentNode.insertBefore(frag, el);
4212                 return el.previousSibling;
4213              case "afterbegin":
4214                 if(el.firstChild){
4215                     range.setStartBefore(el.firstChild);
4216                     frag = range.createContextualFragment(html);
4217                     el.insertBefore(frag, el.firstChild);
4218                     return el.firstChild;
4219                 }else{
4220                     el.innerHTML = html;
4221                     return el.firstChild;
4222                 }
4223             case "beforeend":
4224                 if(el.lastChild){
4225                     range.setStartAfter(el.lastChild);
4226                     frag = range.createContextualFragment(html);
4227                     el.appendChild(frag);
4228                     return el.lastChild;
4229                 }else{
4230                     el.innerHTML = html;
4231                     return el.lastChild;
4232                 }
4233             case "afterend":
4234                 range.setStartAfter(el);
4235                 frag = range.createContextualFragment(html);
4236                 el.parentNode.insertBefore(frag, el.nextSibling);
4237                 return el.nextSibling;
4238             }
4239             throw 'Illegal insertion point -> "' + where + '"';
4240     },
4241
4242     /**
4243      * Creates new Dom element(s) and inserts them before el
4244      * @param {String/HTMLElement/Element} el The context element
4245      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4246      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4247      * @return {HTMLElement/Roo.Element} The new node
4248      */
4249     insertBefore : function(el, o, returnElement){
4250         return this.doInsert(el, o, returnElement, "beforeBegin");
4251     },
4252
4253     /**
4254      * Creates new Dom element(s) and inserts them after el
4255      * @param {String/HTMLElement/Element} el The context element
4256      * @param {Object} o The Dom object spec (and children)
4257      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4258      * @return {HTMLElement/Roo.Element} The new node
4259      */
4260     insertAfter : function(el, o, returnElement){
4261         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4262     },
4263
4264     /**
4265      * Creates new Dom element(s) and inserts them as the first child of el
4266      * @param {String/HTMLElement/Element} el The context element
4267      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4268      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4269      * @return {HTMLElement/Roo.Element} The new node
4270      */
4271     insertFirst : function(el, o, returnElement){
4272         return this.doInsert(el, o, returnElement, "afterBegin");
4273     },
4274
4275     // private
4276     doInsert : function(el, o, returnElement, pos, sibling){
4277         el = Roo.getDom(el);
4278         var newNode;
4279         if(this.useDom || o.ns){
4280             newNode = createDom(o, null);
4281             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4282         }else{
4283             var html = createHtml(o);
4284             newNode = this.insertHtml(pos, el, html);
4285         }
4286         return returnElement ? Roo.get(newNode, true) : newNode;
4287     },
4288
4289     /**
4290      * Creates new Dom element(s) and appends them to el
4291      * @param {String/HTMLElement/Element} el The context element
4292      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4293      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4294      * @return {HTMLElement/Roo.Element} The new node
4295      */
4296     append : function(el, o, returnElement){
4297         el = Roo.getDom(el);
4298         var newNode;
4299         if(this.useDom || o.ns){
4300             newNode = createDom(o, null);
4301             el.appendChild(newNode);
4302         }else{
4303             var html = createHtml(o);
4304             newNode = this.insertHtml("beforeEnd", el, html);
4305         }
4306         return returnElement ? Roo.get(newNode, true) : newNode;
4307     },
4308
4309     /**
4310      * Creates new Dom element(s) and overwrites the contents of el with them
4311      * @param {String/HTMLElement/Element} el The context element
4312      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4313      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4314      * @return {HTMLElement/Roo.Element} The new node
4315      */
4316     overwrite : function(el, o, returnElement){
4317         el = Roo.getDom(el);
4318         if (o.ns) {
4319           
4320             while (el.childNodes.length) {
4321                 el.removeChild(el.firstChild);
4322             }
4323             createDom(o, el);
4324         } else {
4325             el.innerHTML = createHtml(o);   
4326         }
4327         
4328         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4329     },
4330
4331     /**
4332      * Creates a new Roo.DomHelper.Template from the Dom object spec
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {Roo.DomHelper.Template} The new template
4335      */
4336     createTemplate : function(o){
4337         var html = createHtml(o);
4338         return new Roo.Template(html);
4339     }
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.Template
4355 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4356 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4357 * Usage:
4358 <pre><code>
4359 var t = new Roo.Template(
4360     '&lt;div name="{id}"&gt;',
4361         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4362     '&lt;/div&gt;'
4363 );
4364 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4365 </code></pre>
4366 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4367 * @constructor
4368 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4369 */
4370 Roo.Template = function(html){
4371     if(html instanceof Array){
4372         html = html.join("");
4373     }else if(arguments.length > 1){
4374         html = Array.prototype.join.call(arguments, "");
4375     }
4376     /**@private*/
4377     this.html = html;
4378     
4379 };
4380 Roo.Template.prototype = {
4381     /**
4382      * Returns an HTML fragment of this template with the specified values applied.
4383      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4384      * @return {String} The HTML fragment
4385      */
4386     applyTemplate : function(values){
4387         if(this.compiled){
4388             return this.compiled(values);
4389         }
4390         var useF = this.disableFormats !== true;
4391         var fm = Roo.util.Format, tpl = this;
4392         var fn = function(m, name, format, args){
4393             if(format && useF){
4394                 if(format.substr(0, 5) == "this."){
4395                     return tpl.call(format.substr(5), values[name], values);
4396                 }else{
4397                     if(args){
4398                         // quoted values are required for strings in compiled templates, 
4399                         // but for non compiled we need to strip them
4400                         // quoted reversed for jsmin
4401                         var re = /^\s*['"](.*)["']\s*$/;
4402                         args = args.split(',');
4403                         for(var i = 0, len = args.length; i < len; i++){
4404                             args[i] = args[i].replace(re, "$1");
4405                         }
4406                         args = [values[name]].concat(args);
4407                     }else{
4408                         args = [values[name]];
4409                     }
4410                     return fm[format].apply(fm, args);
4411                 }
4412             }else{
4413                 return values[name] !== undefined ? values[name] : "";
4414             }
4415         };
4416         return this.html.replace(this.re, fn);
4417     },
4418     
4419     /**
4420      * Sets the HTML used as the template and optionally compiles it.
4421      * @param {String} html
4422      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4423      * @return {Roo.Template} this
4424      */
4425     set : function(html, compile){
4426         this.html = html;
4427         this.compiled = null;
4428         if(compile){
4429             this.compile();
4430         }
4431         return this;
4432     },
4433     
4434     /**
4435      * True to disable format functions (defaults to false)
4436      * @type Boolean
4437      */
4438     disableFormats : false,
4439     
4440     /**
4441     * The regular expression used to match template variables 
4442     * @type RegExp
4443     * @property 
4444     */
4445     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4446     
4447     /**
4448      * Compiles the template into an internal function, eliminating the RegEx overhead.
4449      * @return {Roo.Template} this
4450      */
4451     compile : function(){
4452         var fm = Roo.util.Format;
4453         var useF = this.disableFormats !== true;
4454         var sep = Roo.isGecko ? "+" : ",";
4455         var fn = function(m, name, format, args){
4456             if(format && useF){
4457                 args = args ? ',' + args : "";
4458                 if(format.substr(0, 5) != "this."){
4459                     format = "fm." + format + '(';
4460                 }else{
4461                     format = 'this.call("'+ format.substr(5) + '", ';
4462                     args = ", values";
4463                 }
4464             }else{
4465                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4466             }
4467             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4468         };
4469         var body;
4470         // branched to use + in gecko and [].join() in others
4471         if(Roo.isGecko){
4472             body = "this.compiled = function(values){ return '" +
4473                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4474                     "';};";
4475         }else{
4476             body = ["this.compiled = function(values){ return ['"];
4477             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4478             body.push("'].join('');};");
4479             body = body.join('');
4480         }
4481         /**
4482          * eval:var:values
4483          * eval:var:fm
4484          */
4485         eval(body);
4486         return this;
4487     },
4488     
4489     // private function used to call members
4490     call : function(fnName, value, allValues){
4491         return this[fnName](value, allValues);
4492     },
4493     
4494     /**
4495      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4496      * @param {String/HTMLElement/Roo.Element} el The context element
4497      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4498      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4499      * @return {HTMLElement/Roo.Element} The new node or Element
4500      */
4501     insertFirst: function(el, values, returnElement){
4502         return this.doInsert('afterBegin', el, values, returnElement);
4503     },
4504
4505     /**
4506      * Applies the supplied values to the template and inserts the new node(s) before el.
4507      * @param {String/HTMLElement/Roo.Element} el The context element
4508      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4510      * @return {HTMLElement/Roo.Element} The new node or Element
4511      */
4512     insertBefore: function(el, values, returnElement){
4513         return this.doInsert('beforeBegin', el, values, returnElement);
4514     },
4515
4516     /**
4517      * Applies the supplied values to the template and inserts the new node(s) after el.
4518      * @param {String/HTMLElement/Roo.Element} el The context element
4519      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4520      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4521      * @return {HTMLElement/Roo.Element} The new node or Element
4522      */
4523     insertAfter : function(el, values, returnElement){
4524         return this.doInsert('afterEnd', el, values, returnElement);
4525     },
4526     
4527     /**
4528      * Applies the supplied values to the template and appends the new node(s) to el.
4529      * @param {String/HTMLElement/Roo.Element} el The context element
4530      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4531      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4532      * @return {HTMLElement/Roo.Element} The new node or Element
4533      */
4534     append : function(el, values, returnElement){
4535         return this.doInsert('beforeEnd', el, values, returnElement);
4536     },
4537
4538     doInsert : function(where, el, values, returnEl){
4539         el = Roo.getDom(el);
4540         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4541         return returnEl ? Roo.get(newNode, true) : newNode;
4542     },
4543
4544     /**
4545      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4546      * @param {String/HTMLElement/Roo.Element} el The context element
4547      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4548      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4549      * @return {HTMLElement/Roo.Element} The new node or Element
4550      */
4551     overwrite : function(el, values, returnElement){
4552         el = Roo.getDom(el);
4553         el.innerHTML = this.applyTemplate(values);
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     }
4556 };
4557 /**
4558  * Alias for {@link #applyTemplate}
4559  * @method
4560  */
4561 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4562
4563 // backwards compat
4564 Roo.DomHelper.Template = Roo.Template;
4565
4566 /**
4567  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4568  * @param {String/HTMLElement} el A DOM element or its id
4569  * @returns {Roo.Template} The created template
4570  * @static
4571  */
4572 Roo.Template.from = function(el){
4573     el = Roo.getDom(el);
4574     return new Roo.Template(el.value || el.innerHTML);
4575 };/*
4576  * Based on:
4577  * Ext JS Library 1.1.1
4578  * Copyright(c) 2006-2007, Ext JS, LLC.
4579  *
4580  * Originally Released Under LGPL - original licence link has changed is not relivant.
4581  *
4582  * Fork - LGPL
4583  * <script type="text/javascript">
4584  */
4585  
4586
4587 /*
4588  * This is code is also distributed under MIT license for use
4589  * with jQuery and prototype JavaScript libraries.
4590  */
4591 /**
4592  * @class Roo.DomQuery
4593 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4594 <p>
4595 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4596
4597 <p>
4598 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4599 </p>
4600 <h4>Element Selectors:</h4>
4601 <ul class="list">
4602     <li> <b>*</b> any element</li>
4603     <li> <b>E</b> an element with the tag E</li>
4604     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4605     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4606     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4607     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4608 </ul>
4609 <h4>Attribute Selectors:</h4>
4610 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4611 <ul class="list">
4612     <li> <b>E[foo]</b> has an attribute "foo"</li>
4613     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4614     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4615     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4616     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4617     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4618     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4619 </ul>
4620 <h4>Pseudo Classes:</h4>
4621 <ul class="list">
4622     <li> <b>E:first-child</b> E is the first child of its parent</li>
4623     <li> <b>E:last-child</b> E is the last child of its parent</li>
4624     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4625     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4626     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4627     <li> <b>E:only-child</b> E is the only child of its parent</li>
4628     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4629     <li> <b>E:first</b> the first E in the resultset</li>
4630     <li> <b>E:last</b> the last E in the resultset</li>
4631     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4632     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4633     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4634     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4635     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4636     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4637     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4638     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4639     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4640 </ul>
4641 <h4>CSS Value Selectors:</h4>
4642 <ul class="list">
4643     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4644     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4645     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4646     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4647     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4648     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4649 </ul>
4650  * @singleton
4651  */
4652 Roo.DomQuery = function(){
4653     var cache = {}, simpleCache = {}, valueCache = {};
4654     var nonSpace = /\S/;
4655     var trimRe = /^\s+|\s+$/g;
4656     var tplRe = /\{(\d+)\}/g;
4657     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4658     var tagTokenRe = /^(#)?([\w-\*]+)/;
4659     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4660
4661     function child(p, index){
4662         var i = 0;
4663         var n = p.firstChild;
4664         while(n){
4665             if(n.nodeType == 1){
4666                if(++i == index){
4667                    return n;
4668                }
4669             }
4670             n = n.nextSibling;
4671         }
4672         return null;
4673     };
4674
4675     function next(n){
4676         while((n = n.nextSibling) && n.nodeType != 1);
4677         return n;
4678     };
4679
4680     function prev(n){
4681         while((n = n.previousSibling) && n.nodeType != 1);
4682         return n;
4683     };
4684
4685     function children(d){
4686         var n = d.firstChild, ni = -1;
4687             while(n){
4688                 var nx = n.nextSibling;
4689                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4690                     d.removeChild(n);
4691                 }else{
4692                     n.nodeIndex = ++ni;
4693                 }
4694                 n = nx;
4695             }
4696             return this;
4697         };
4698
4699     function byClassName(c, a, v){
4700         if(!v){
4701             return c;
4702         }
4703         var r = [], ri = -1, cn;
4704         for(var i = 0, ci; ci = c[i]; i++){
4705             if((' '+ci.className+' ').indexOf(v) != -1){
4706                 r[++ri] = ci;
4707             }
4708         }
4709         return r;
4710     };
4711
4712     function attrValue(n, attr){
4713         if(!n.tagName && typeof n.length != "undefined"){
4714             n = n[0];
4715         }
4716         if(!n){
4717             return null;
4718         }
4719         if(attr == "for"){
4720             return n.htmlFor;
4721         }
4722         if(attr == "class" || attr == "className"){
4723             return n.className;
4724         }
4725         return n.getAttribute(attr) || n[attr];
4726
4727     };
4728
4729     function getNodes(ns, mode, tagName){
4730         var result = [], ri = -1, cs;
4731         if(!ns){
4732             return result;
4733         }
4734         tagName = tagName || "*";
4735         if(typeof ns.getElementsByTagName != "undefined"){
4736             ns = [ns];
4737         }
4738         if(!mode){
4739             for(var i = 0, ni; ni = ns[i]; i++){
4740                 cs = ni.getElementsByTagName(tagName);
4741                 for(var j = 0, ci; ci = cs[j]; j++){
4742                     result[++ri] = ci;
4743                 }
4744             }
4745         }else if(mode == "/" || mode == ">"){
4746             var utag = tagName.toUpperCase();
4747             for(var i = 0, ni, cn; ni = ns[i]; i++){
4748                 cn = ni.children || ni.childNodes;
4749                 for(var j = 0, cj; cj = cn[j]; j++){
4750                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4751                         result[++ri] = cj;
4752                     }
4753                 }
4754             }
4755         }else if(mode == "+"){
4756             var utag = tagName.toUpperCase();
4757             for(var i = 0, n; n = ns[i]; i++){
4758                 while((n = n.nextSibling) && n.nodeType != 1);
4759                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4760                     result[++ri] = n;
4761                 }
4762             }
4763         }else if(mode == "~"){
4764             for(var i = 0, n; n = ns[i]; i++){
4765                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4766                 if(n){
4767                     result[++ri] = n;
4768                 }
4769             }
4770         }
4771         return result;
4772     };
4773
4774     function concat(a, b){
4775         if(b.slice){
4776             return a.concat(b);
4777         }
4778         for(var i = 0, l = b.length; i < l; i++){
4779             a[a.length] = b[i];
4780         }
4781         return a;
4782     }
4783
4784     function byTag(cs, tagName){
4785         if(cs.tagName || cs == document){
4786             cs = [cs];
4787         }
4788         if(!tagName){
4789             return cs;
4790         }
4791         var r = [], ri = -1;
4792         tagName = tagName.toLowerCase();
4793         for(var i = 0, ci; ci = cs[i]; i++){
4794             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4795                 r[++ri] = ci;
4796             }
4797         }
4798         return r;
4799     };
4800
4801     function byId(cs, attr, id){
4802         if(cs.tagName || cs == document){
4803             cs = [cs];
4804         }
4805         if(!id){
4806             return cs;
4807         }
4808         var r = [], ri = -1;
4809         for(var i = 0,ci; ci = cs[i]; i++){
4810             if(ci && ci.id == id){
4811                 r[++ri] = ci;
4812                 return r;
4813             }
4814         }
4815         return r;
4816     };
4817
4818     function byAttribute(cs, attr, value, op, custom){
4819         var r = [], ri = -1, st = custom=="{";
4820         var f = Roo.DomQuery.operators[op];
4821         for(var i = 0, ci; ci = cs[i]; i++){
4822             var a;
4823             if(st){
4824                 a = Roo.DomQuery.getStyle(ci, attr);
4825             }
4826             else if(attr == "class" || attr == "className"){
4827                 a = ci.className;
4828             }else if(attr == "for"){
4829                 a = ci.htmlFor;
4830             }else if(attr == "href"){
4831                 a = ci.getAttribute("href", 2);
4832             }else{
4833                 a = ci.getAttribute(attr);
4834             }
4835             if((f && f(a, value)) || (!f && a)){
4836                 r[++ri] = ci;
4837             }
4838         }
4839         return r;
4840     };
4841
4842     function byPseudo(cs, name, value){
4843         return Roo.DomQuery.pseudos[name](cs, value);
4844     };
4845
4846     // This is for IE MSXML which does not support expandos.
4847     // IE runs the same speed using setAttribute, however FF slows way down
4848     // and Safari completely fails so they need to continue to use expandos.
4849     var isIE = window.ActiveXObject ? true : false;
4850
4851     // this eval is stop the compressor from
4852     // renaming the variable to something shorter
4853     
4854     /** eval:var:batch */
4855     var batch = 30803; 
4856
4857     var key = 30803;
4858
4859     function nodupIEXml(cs){
4860         var d = ++key;
4861         cs[0].setAttribute("_nodup", d);
4862         var r = [cs[0]];
4863         for(var i = 1, len = cs.length; i < len; i++){
4864             var c = cs[i];
4865             if(!c.getAttribute("_nodup") != d){
4866                 c.setAttribute("_nodup", d);
4867                 r[r.length] = c;
4868             }
4869         }
4870         for(var i = 0, len = cs.length; i < len; i++){
4871             cs[i].removeAttribute("_nodup");
4872         }
4873         return r;
4874     }
4875
4876     function nodup(cs){
4877         if(!cs){
4878             return [];
4879         }
4880         var len = cs.length, c, i, r = cs, cj, ri = -1;
4881         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4882             return cs;
4883         }
4884         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4885             return nodupIEXml(cs);
4886         }
4887         var d = ++key;
4888         cs[0]._nodup = d;
4889         for(i = 1; c = cs[i]; i++){
4890             if(c._nodup != d){
4891                 c._nodup = d;
4892             }else{
4893                 r = [];
4894                 for(var j = 0; j < i; j++){
4895                     r[++ri] = cs[j];
4896                 }
4897                 for(j = i+1; cj = cs[j]; j++){
4898                     if(cj._nodup != d){
4899                         cj._nodup = d;
4900                         r[++ri] = cj;
4901                     }
4902                 }
4903                 return r;
4904             }
4905         }
4906         return r;
4907     }
4908
4909     function quickDiffIEXml(c1, c2){
4910         var d = ++key;
4911         for(var i = 0, len = c1.length; i < len; i++){
4912             c1[i].setAttribute("_qdiff", d);
4913         }
4914         var r = [];
4915         for(var i = 0, len = c2.length; i < len; i++){
4916             if(c2[i].getAttribute("_qdiff") != d){
4917                 r[r.length] = c2[i];
4918             }
4919         }
4920         for(var i = 0, len = c1.length; i < len; i++){
4921            c1[i].removeAttribute("_qdiff");
4922         }
4923         return r;
4924     }
4925
4926     function quickDiff(c1, c2){
4927         var len1 = c1.length;
4928         if(!len1){
4929             return c2;
4930         }
4931         if(isIE && c1[0].selectSingleNode){
4932             return quickDiffIEXml(c1, c2);
4933         }
4934         var d = ++key;
4935         for(var i = 0; i < len1; i++){
4936             c1[i]._qdiff = d;
4937         }
4938         var r = [];
4939         for(var i = 0, len = c2.length; i < len; i++){
4940             if(c2[i]._qdiff != d){
4941                 r[r.length] = c2[i];
4942             }
4943         }
4944         return r;
4945     }
4946
4947     function quickId(ns, mode, root, id){
4948         if(ns == root){
4949            var d = root.ownerDocument || root;
4950            return d.getElementById(id);
4951         }
4952         ns = getNodes(ns, mode, "*");
4953         return byId(ns, null, id);
4954     }
4955
4956     return {
4957         getStyle : function(el, name){
4958             return Roo.fly(el).getStyle(name);
4959         },
4960         /**
4961          * Compiles a selector/xpath query into a reusable function. The returned function
4962          * takes one parameter "root" (optional), which is the context node from where the query should start.
4963          * @param {String} selector The selector/xpath query
4964          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4965          * @return {Function}
4966          */
4967         compile : function(path, type){
4968             type = type || "select";
4969             
4970             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4971             var q = path, mode, lq;
4972             var tk = Roo.DomQuery.matchers;
4973             var tklen = tk.length;
4974             var mm;
4975
4976             // accept leading mode switch
4977             var lmode = q.match(modeRe);
4978             if(lmode && lmode[1]){
4979                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
4980                 q = q.replace(lmode[1], "");
4981             }
4982             // strip leading slashes
4983             while(path.substr(0, 1)=="/"){
4984                 path = path.substr(1);
4985             }
4986
4987             while(q && lq != q){
4988                 lq = q;
4989                 var tm = q.match(tagTokenRe);
4990                 if(type == "select"){
4991                     if(tm){
4992                         if(tm[1] == "#"){
4993                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
4994                         }else{
4995                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
4996                         }
4997                         q = q.replace(tm[0], "");
4998                     }else if(q.substr(0, 1) != '@'){
4999                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5000                     }
5001                 }else{
5002                     if(tm){
5003                         if(tm[1] == "#"){
5004                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5005                         }else{
5006                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5007                         }
5008                         q = q.replace(tm[0], "");
5009                     }
5010                 }
5011                 while(!(mm = q.match(modeRe))){
5012                     var matched = false;
5013                     for(var j = 0; j < tklen; j++){
5014                         var t = tk[j];
5015                         var m = q.match(t.re);
5016                         if(m){
5017                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5018                                                     return m[i];
5019                                                 });
5020                             q = q.replace(m[0], "");
5021                             matched = true;
5022                             break;
5023                         }
5024                     }
5025                     // prevent infinite loop on bad selector
5026                     if(!matched){
5027                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5028                     }
5029                 }
5030                 if(mm[1]){
5031                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5032                     q = q.replace(mm[1], "");
5033                 }
5034             }
5035             fn[fn.length] = "return nodup(n);\n}";
5036             
5037              /** 
5038               * list of variables that need from compression as they are used by eval.
5039              *  eval:var:batch 
5040              *  eval:var:nodup
5041              *  eval:var:byTag
5042              *  eval:var:ById
5043              *  eval:var:getNodes
5044              *  eval:var:quickId
5045              *  eval:var:mode
5046              *  eval:var:root
5047              *  eval:var:n
5048              *  eval:var:byClassName
5049              *  eval:var:byPseudo
5050              *  eval:var:byAttribute
5051              *  eval:var:attrValue
5052              * 
5053              **/ 
5054             eval(fn.join(""));
5055             return f;
5056         },
5057
5058         /**
5059          * Selects a group of elements.
5060          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5061          * @param {Node} root (optional) The start of the query (defaults to document).
5062          * @return {Array}
5063          */
5064         select : function(path, root, type){
5065             if(!root || root == document){
5066                 root = document;
5067             }
5068             if(typeof root == "string"){
5069                 root = document.getElementById(root);
5070             }
5071             var paths = path.split(",");
5072             var results = [];
5073             for(var i = 0, len = paths.length; i < len; i++){
5074                 var p = paths[i].replace(trimRe, "");
5075                 if(!cache[p]){
5076                     cache[p] = Roo.DomQuery.compile(p);
5077                     if(!cache[p]){
5078                         throw p + " is not a valid selector";
5079                     }
5080                 }
5081                 var result = cache[p](root);
5082                 if(result && result != document){
5083                     results = results.concat(result);
5084                 }
5085             }
5086             if(paths.length > 1){
5087                 return nodup(results);
5088             }
5089             return results;
5090         },
5091
5092         /**
5093          * Selects a single element.
5094          * @param {String} selector The selector/xpath query
5095          * @param {Node} root (optional) The start of the query (defaults to document).
5096          * @return {Element}
5097          */
5098         selectNode : function(path, root){
5099             return Roo.DomQuery.select(path, root)[0];
5100         },
5101
5102         /**
5103          * Selects the value of a node, optionally replacing null with the defaultValue.
5104          * @param {String} selector The selector/xpath query
5105          * @param {Node} root (optional) The start of the query (defaults to document).
5106          * @param {String} defaultValue
5107          */
5108         selectValue : function(path, root, defaultValue){
5109             path = path.replace(trimRe, "");
5110             if(!valueCache[path]){
5111                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5112             }
5113             var n = valueCache[path](root);
5114             n = n[0] ? n[0] : n;
5115             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5116             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5117         },
5118
5119         /**
5120          * Selects the value of a node, parsing integers and floats.
5121          * @param {String} selector The selector/xpath query
5122          * @param {Node} root (optional) The start of the query (defaults to document).
5123          * @param {Number} defaultValue
5124          * @return {Number}
5125          */
5126         selectNumber : function(path, root, defaultValue){
5127             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5128             return parseFloat(v);
5129         },
5130
5131         /**
5132          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5133          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5134          * @param {String} selector The simple selector to test
5135          * @return {Boolean}
5136          */
5137         is : function(el, ss){
5138             if(typeof el == "string"){
5139                 el = document.getElementById(el);
5140             }
5141             var isArray = (el instanceof Array);
5142             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5143             return isArray ? (result.length == el.length) : (result.length > 0);
5144         },
5145
5146         /**
5147          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5148          * @param {Array} el An array of elements to filter
5149          * @param {String} selector The simple selector to test
5150          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5151          * the selector instead of the ones that match
5152          * @return {Array}
5153          */
5154         filter : function(els, ss, nonMatches){
5155             ss = ss.replace(trimRe, "");
5156             if(!simpleCache[ss]){
5157                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5158             }
5159             var result = simpleCache[ss](els);
5160             return nonMatches ? quickDiff(result, els) : result;
5161         },
5162
5163         /**
5164          * Collection of matching regular expressions and code snippets.
5165          */
5166         matchers : [{
5167                 re: /^\.([\w-]+)/,
5168                 select: 'n = byClassName(n, null, " {1} ");'
5169             }, {
5170                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5171                 select: 'n = byPseudo(n, "{1}", "{2}");'
5172             },{
5173                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5174                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5175             }, {
5176                 re: /^#([\w-]+)/,
5177                 select: 'n = byId(n, null, "{1}");'
5178             },{
5179                 re: /^@([\w-]+)/,
5180                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5181             }
5182         ],
5183
5184         /**
5185          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5186          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5187          */
5188         operators : {
5189             "=" : function(a, v){
5190                 return a == v;
5191             },
5192             "!=" : function(a, v){
5193                 return a != v;
5194             },
5195             "^=" : function(a, v){
5196                 return a && a.substr(0, v.length) == v;
5197             },
5198             "$=" : function(a, v){
5199                 return a && a.substr(a.length-v.length) == v;
5200             },
5201             "*=" : function(a, v){
5202                 return a && a.indexOf(v) !== -1;
5203             },
5204             "%=" : function(a, v){
5205                 return (a % v) == 0;
5206             },
5207             "|=" : function(a, v){
5208                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5209             },
5210             "~=" : function(a, v){
5211                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5212             }
5213         },
5214
5215         /**
5216          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5217          * and the argument (if any) supplied in the selector.
5218          */
5219         pseudos : {
5220             "first-child" : function(c){
5221                 var r = [], ri = -1, n;
5222                 for(var i = 0, ci; ci = n = c[i]; i++){
5223                     while((n = n.previousSibling) && n.nodeType != 1);
5224                     if(!n){
5225                         r[++ri] = ci;
5226                     }
5227                 }
5228                 return r;
5229             },
5230
5231             "last-child" : function(c){
5232                 var r = [], ri = -1, n;
5233                 for(var i = 0, ci; ci = n = c[i]; i++){
5234                     while((n = n.nextSibling) && n.nodeType != 1);
5235                     if(!n){
5236                         r[++ri] = ci;
5237                     }
5238                 }
5239                 return r;
5240             },
5241
5242             "nth-child" : function(c, a) {
5243                 var r = [], ri = -1;
5244                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5245                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5246                 for(var i = 0, n; n = c[i]; i++){
5247                     var pn = n.parentNode;
5248                     if (batch != pn._batch) {
5249                         var j = 0;
5250                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5251                             if(cn.nodeType == 1){
5252                                cn.nodeIndex = ++j;
5253                             }
5254                         }
5255                         pn._batch = batch;
5256                     }
5257                     if (f == 1) {
5258                         if (l == 0 || n.nodeIndex == l){
5259                             r[++ri] = n;
5260                         }
5261                     } else if ((n.nodeIndex + l) % f == 0){
5262                         r[++ri] = n;
5263                     }
5264                 }
5265
5266                 return r;
5267             },
5268
5269             "only-child" : function(c){
5270                 var r = [], ri = -1;;
5271                 for(var i = 0, ci; ci = c[i]; i++){
5272                     if(!prev(ci) && !next(ci)){
5273                         r[++ri] = ci;
5274                     }
5275                 }
5276                 return r;
5277             },
5278
5279             "empty" : function(c){
5280                 var r = [], ri = -1;
5281                 for(var i = 0, ci; ci = c[i]; i++){
5282                     var cns = ci.childNodes, j = 0, cn, empty = true;
5283                     while(cn = cns[j]){
5284                         ++j;
5285                         if(cn.nodeType == 1 || cn.nodeType == 3){
5286                             empty = false;
5287                             break;
5288                         }
5289                     }
5290                     if(empty){
5291                         r[++ri] = ci;
5292                     }
5293                 }
5294                 return r;
5295             },
5296
5297             "contains" : function(c, v){
5298                 var r = [], ri = -1;
5299                 for(var i = 0, ci; ci = c[i]; i++){
5300                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5301                         r[++ri] = ci;
5302                     }
5303                 }
5304                 return r;
5305             },
5306
5307             "nodeValue" : function(c, v){
5308                 var r = [], ri = -1;
5309                 for(var i = 0, ci; ci = c[i]; i++){
5310                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5311                         r[++ri] = ci;
5312                     }
5313                 }
5314                 return r;
5315             },
5316
5317             "checked" : function(c){
5318                 var r = [], ri = -1;
5319                 for(var i = 0, ci; ci = c[i]; i++){
5320                     if(ci.checked == true){
5321                         r[++ri] = ci;
5322                     }
5323                 }
5324                 return r;
5325             },
5326
5327             "not" : function(c, ss){
5328                 return Roo.DomQuery.filter(c, ss, true);
5329             },
5330
5331             "odd" : function(c){
5332                 return this["nth-child"](c, "odd");
5333             },
5334
5335             "even" : function(c){
5336                 return this["nth-child"](c, "even");
5337             },
5338
5339             "nth" : function(c, a){
5340                 return c[a-1] || [];
5341             },
5342
5343             "first" : function(c){
5344                 return c[0] || [];
5345             },
5346
5347             "last" : function(c){
5348                 return c[c.length-1] || [];
5349             },
5350
5351             "has" : function(c, ss){
5352                 var s = Roo.DomQuery.select;
5353                 var r = [], ri = -1;
5354                 for(var i = 0, ci; ci = c[i]; i++){
5355                     if(s(ss, ci).length > 0){
5356                         r[++ri] = ci;
5357                     }
5358                 }
5359                 return r;
5360             },
5361
5362             "next" : function(c, ss){
5363                 var is = Roo.DomQuery.is;
5364                 var r = [], ri = -1;
5365                 for(var i = 0, ci; ci = c[i]; i++){
5366                     var n = next(ci);
5367                     if(n && is(n, ss)){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "prev" : function(c, ss){
5375                 var is = Roo.DomQuery.is;
5376                 var r = [], ri = -1;
5377                 for(var i = 0, ci; ci = c[i]; i++){
5378                     var n = prev(ci);
5379                     if(n && is(n, ss)){
5380                         r[++ri] = ci;
5381                     }
5382                 }
5383                 return r;
5384             }
5385         }
5386     };
5387 }();
5388
5389 /**
5390  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5391  * @param {String} path The selector/xpath query
5392  * @param {Node} root (optional) The start of the query (defaults to document).
5393  * @return {Array}
5394  * @member Roo
5395  * @method query
5396  */
5397 Roo.query = Roo.DomQuery.select;
5398 /*
5399  * Based on:
5400  * Ext JS Library 1.1.1
5401  * Copyright(c) 2006-2007, Ext JS, LLC.
5402  *
5403  * Originally Released Under LGPL - original licence link has changed is not relivant.
5404  *
5405  * Fork - LGPL
5406  * <script type="text/javascript">
5407  */
5408
5409 /**
5410  * @class Roo.util.Observable
5411  * Base class that provides a common interface for publishing events. Subclasses are expected to
5412  * to have a property "events" with all the events defined.<br>
5413  * For example:
5414  * <pre><code>
5415  Employee = function(name){
5416     this.name = name;
5417     this.addEvents({
5418         "fired" : true,
5419         "quit" : true
5420     });
5421  }
5422  Roo.extend(Employee, Roo.util.Observable);
5423 </code></pre>
5424  * @param {Object} config properties to use (incuding events / listeners)
5425  */
5426
5427 Roo.util.Observable = function(cfg){
5428     
5429     cfg = cfg|| {};
5430     this.addEvents(cfg.events || {});
5431     if (cfg.events) {
5432         delete cfg.events; // make sure
5433     }
5434      
5435     Roo.apply(this, cfg);
5436     
5437     if(this.listeners){
5438         this.on(this.listeners);
5439         delete this.listeners;
5440     }
5441 };
5442 Roo.util.Observable.prototype = {
5443     /** 
5444  * @cfg {Object} listeners  list of events and functions to call for this object, 
5445  * For example :
5446  * <pre><code>
5447     listeners :  { 
5448        'click' : function(e) {
5449            ..... 
5450         } ,
5451         .... 
5452     } 
5453   </code></pre>
5454  */
5455     
5456     
5457     /**
5458      * Fires the specified event with the passed parameters (minus the event name).
5459      * @param {String} eventName
5460      * @param {Object...} args Variable number of parameters are passed to handlers
5461      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5462      */
5463     fireEvent : function(){
5464         var ce = this.events[arguments[0].toLowerCase()];
5465         if(typeof ce == "object"){
5466             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5467         }else{
5468             return true;
5469         }
5470     },
5471
5472     // private
5473     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5474
5475     /**
5476      * Appends an event handler to this component
5477      * @param {String}   eventName The type of event to listen for
5478      * @param {Function} handler The method the event invokes
5479      * @param {Object}   scope (optional) The scope in which to execute the handler
5480      * function. The handler function's "this" context.
5481      * @param {Object}   options (optional) An object containing handler configuration
5482      * properties. This may contain any of the following properties:<ul>
5483      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5484      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5485      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5486      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5487      * by the specified number of milliseconds. If the event fires again within that time, the original
5488      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5489      * </ul><br>
5490      * <p>
5491      * <b>Combining Options</b><br>
5492      * Using the options argument, it is possible to combine different types of listeners:<br>
5493      * <br>
5494      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5495                 <pre><code>
5496                 el.on('click', this.onClick, this, {
5497                         single: true,
5498                 delay: 100,
5499                 forumId: 4
5500                 });
5501                 </code></pre>
5502      * <p>
5503      * <b>Attaching multiple handlers in 1 call</b><br>
5504      * The method also allows for a single argument to be passed which is a config object containing properties
5505      * which specify multiple handlers.
5506      * <pre><code>
5507                 el.on({
5508                         'click': {
5509                         fn: this.onClick,
5510                         scope: this,
5511                         delay: 100
5512                 }, 
5513                 'mouseover': {
5514                         fn: this.onMouseOver,
5515                         scope: this
5516                 },
5517                 'mouseout': {
5518                         fn: this.onMouseOut,
5519                         scope: this
5520                 }
5521                 });
5522                 </code></pre>
5523      * <p>
5524      * Or a shorthand syntax which passes the same scope object to all handlers:
5525         <pre><code>
5526                 el.on({
5527                         'click': this.onClick,
5528                 'mouseover': this.onMouseOver,
5529                 'mouseout': this.onMouseOut,
5530                 scope: this
5531                 });
5532                 </code></pre>
5533      */
5534     addListener : function(eventName, fn, scope, o){
5535         if(typeof eventName == "object"){
5536             o = eventName;
5537             for(var e in o){
5538                 if(this.filterOptRe.test(e)){
5539                     continue;
5540                 }
5541                 if(typeof o[e] == "function"){
5542                     // shared options
5543                     this.addListener(e, o[e], o.scope,  o);
5544                 }else{
5545                     // individual options
5546                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5547                 }
5548             }
5549             return;
5550         }
5551         o = (!o || typeof o == "boolean") ? {} : o;
5552         eventName = eventName.toLowerCase();
5553         var ce = this.events[eventName] || true;
5554         if(typeof ce == "boolean"){
5555             ce = new Roo.util.Event(this, eventName);
5556             this.events[eventName] = ce;
5557         }
5558         ce.addListener(fn, scope, o);
5559     },
5560
5561     /**
5562      * Removes a listener
5563      * @param {String}   eventName     The type of event to listen for
5564      * @param {Function} handler        The handler to remove
5565      * @param {Object}   scope  (optional) The scope (this object) for the handler
5566      */
5567     removeListener : function(eventName, fn, scope){
5568         var ce = this.events[eventName.toLowerCase()];
5569         if(typeof ce == "object"){
5570             ce.removeListener(fn, scope);
5571         }
5572     },
5573
5574     /**
5575      * Removes all listeners for this object
5576      */
5577     purgeListeners : function(){
5578         for(var evt in this.events){
5579             if(typeof this.events[evt] == "object"){
5580                  this.events[evt].clearListeners();
5581             }
5582         }
5583     },
5584
5585     relayEvents : function(o, events){
5586         var createHandler = function(ename){
5587             return function(){
5588                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5589             };
5590         };
5591         for(var i = 0, len = events.length; i < len; i++){
5592             var ename = events[i];
5593             if(!this.events[ename]){ this.events[ename] = true; };
5594             o.on(ename, createHandler(ename), this);
5595         }
5596     },
5597
5598     /**
5599      * Used to define events on this Observable
5600      * @param {Object} object The object with the events defined
5601      */
5602     addEvents : function(o){
5603         if(!this.events){
5604             this.events = {};
5605         }
5606         Roo.applyIf(this.events, o);
5607     },
5608
5609     /**
5610      * Checks to see if this object has any listeners for a specified event
5611      * @param {String} eventName The name of the event to check for
5612      * @return {Boolean} True if the event is being listened for, else false
5613      */
5614     hasListener : function(eventName){
5615         var e = this.events[eventName];
5616         return typeof e == "object" && e.listeners.length > 0;
5617     }
5618 };
5619 /**
5620  * Appends an event handler to this element (shorthand for addListener)
5621  * @param {String}   eventName     The type of event to listen for
5622  * @param {Function} handler        The method the event invokes
5623  * @param {Object}   scope (optional) The scope in which to execute the handler
5624  * function. The handler function's "this" context.
5625  * @param {Object}   options  (optional)
5626  * @method
5627  */
5628 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5629 /**
5630  * Removes a listener (shorthand for removeListener)
5631  * @param {String}   eventName     The type of event to listen for
5632  * @param {Function} handler        The handler to remove
5633  * @param {Object}   scope  (optional) The scope (this object) for the handler
5634  * @method
5635  */
5636 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5637
5638 /**
5639  * Starts capture on the specified Observable. All events will be passed
5640  * to the supplied function with the event name + standard signature of the event
5641  * <b>before</b> the event is fired. If the supplied function returns false,
5642  * the event will not fire.
5643  * @param {Observable} o The Observable to capture
5644  * @param {Function} fn The function to call
5645  * @param {Object} scope (optional) The scope (this object) for the fn
5646  * @static
5647  */
5648 Roo.util.Observable.capture = function(o, fn, scope){
5649     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5650 };
5651
5652 /**
5653  * Removes <b>all</b> added captures from the Observable.
5654  * @param {Observable} o The Observable to release
5655  * @static
5656  */
5657 Roo.util.Observable.releaseCapture = function(o){
5658     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5659 };
5660
5661 (function(){
5662
5663     var createBuffered = function(h, o, scope){
5664         var task = new Roo.util.DelayedTask();
5665         return function(){
5666             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5667         };
5668     };
5669
5670     var createSingle = function(h, e, fn, scope){
5671         return function(){
5672             e.removeListener(fn, scope);
5673             return h.apply(scope, arguments);
5674         };
5675     };
5676
5677     var createDelayed = function(h, o, scope){
5678         return function(){
5679             var args = Array.prototype.slice.call(arguments, 0);
5680             setTimeout(function(){
5681                 h.apply(scope, args);
5682             }, o.delay || 10);
5683         };
5684     };
5685
5686     Roo.util.Event = function(obj, name){
5687         this.name = name;
5688         this.obj = obj;
5689         this.listeners = [];
5690     };
5691
5692     Roo.util.Event.prototype = {
5693         addListener : function(fn, scope, options){
5694             var o = options || {};
5695             scope = scope || this.obj;
5696             if(!this.isListening(fn, scope)){
5697                 var l = {fn: fn, scope: scope, options: o};
5698                 var h = fn;
5699                 if(o.delay){
5700                     h = createDelayed(h, o, scope);
5701                 }
5702                 if(o.single){
5703                     h = createSingle(h, this, fn, scope);
5704                 }
5705                 if(o.buffer){
5706                     h = createBuffered(h, o, scope);
5707                 }
5708                 l.fireFn = h;
5709                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5710                     this.listeners.push(l);
5711                 }else{
5712                     this.listeners = this.listeners.slice(0);
5713                     this.listeners.push(l);
5714                 }
5715             }
5716         },
5717
5718         findListener : function(fn, scope){
5719             scope = scope || this.obj;
5720             var ls = this.listeners;
5721             for(var i = 0, len = ls.length; i < len; i++){
5722                 var l = ls[i];
5723                 if(l.fn == fn && l.scope == scope){
5724                     return i;
5725                 }
5726             }
5727             return -1;
5728         },
5729
5730         isListening : function(fn, scope){
5731             return this.findListener(fn, scope) != -1;
5732         },
5733
5734         removeListener : function(fn, scope){
5735             var index;
5736             if((index = this.findListener(fn, scope)) != -1){
5737                 if(!this.firing){
5738                     this.listeners.splice(index, 1);
5739                 }else{
5740                     this.listeners = this.listeners.slice(0);
5741                     this.listeners.splice(index, 1);
5742                 }
5743                 return true;
5744             }
5745             return false;
5746         },
5747
5748         clearListeners : function(){
5749             this.listeners = [];
5750         },
5751
5752         fire : function(){
5753             var ls = this.listeners, scope, len = ls.length;
5754             if(len > 0){
5755                 this.firing = true;
5756                 var args = Array.prototype.slice.call(arguments, 0);
5757                 for(var i = 0; i < len; i++){
5758                     var l = ls[i];
5759                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5760                         this.firing = false;
5761                         return false;
5762                     }
5763                 }
5764                 this.firing = false;
5765             }
5766             return true;
5767         }
5768     };
5769 })();/*
5770  * Based on:
5771  * Ext JS Library 1.1.1
5772  * Copyright(c) 2006-2007, Ext JS, LLC.
5773  *
5774  * Originally Released Under LGPL - original licence link has changed is not relivant.
5775  *
5776  * Fork - LGPL
5777  * <script type="text/javascript">
5778  */
5779
5780 /**
5781  * @class Roo.EventManager
5782  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5783  * several useful events directly.
5784  * See {@link Roo.EventObject} for more details on normalized event objects.
5785  * @singleton
5786  */
5787 Roo.EventManager = function(){
5788     var docReadyEvent, docReadyProcId, docReadyState = false;
5789     var resizeEvent, resizeTask, textEvent, textSize;
5790     var E = Roo.lib.Event;
5791     var D = Roo.lib.Dom;
5792
5793
5794     var fireDocReady = function(){
5795         if(!docReadyState){
5796             docReadyState = true;
5797             Roo.isReady = true;
5798             if(docReadyProcId){
5799                 clearInterval(docReadyProcId);
5800             }
5801             if(Roo.isGecko || Roo.isOpera) {
5802                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5803             }
5804             if(Roo.isIE){
5805                 var defer = document.getElementById("ie-deferred-loader");
5806                 if(defer){
5807                     defer.onreadystatechange = null;
5808                     defer.parentNode.removeChild(defer);
5809                 }
5810             }
5811             if(docReadyEvent){
5812                 docReadyEvent.fire();
5813                 docReadyEvent.clearListeners();
5814             }
5815         }
5816     };
5817     
5818     var initDocReady = function(){
5819         docReadyEvent = new Roo.util.Event();
5820         if(Roo.isGecko || Roo.isOpera) {
5821             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5822         }else if(Roo.isIE){
5823             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5824             var defer = document.getElementById("ie-deferred-loader");
5825             defer.onreadystatechange = function(){
5826                 if(this.readyState == "complete"){
5827                     fireDocReady();
5828                 }
5829             };
5830         }else if(Roo.isSafari){ 
5831             docReadyProcId = setInterval(function(){
5832                 var rs = document.readyState;
5833                 if(rs == "complete") {
5834                     fireDocReady();     
5835                  }
5836             }, 10);
5837         }
5838         // no matter what, make sure it fires on load
5839         E.on(window, "load", fireDocReady);
5840     };
5841
5842     var createBuffered = function(h, o){
5843         var task = new Roo.util.DelayedTask(h);
5844         return function(e){
5845             // create new event object impl so new events don't wipe out properties
5846             e = new Roo.EventObjectImpl(e);
5847             task.delay(o.buffer, h, null, [e]);
5848         };
5849     };
5850
5851     var createSingle = function(h, el, ename, fn){
5852         return function(e){
5853             Roo.EventManager.removeListener(el, ename, fn);
5854             h(e);
5855         };
5856     };
5857
5858     var createDelayed = function(h, o){
5859         return function(e){
5860             // create new event object impl so new events don't wipe out properties
5861             e = new Roo.EventObjectImpl(e);
5862             setTimeout(function(){
5863                 h(e);
5864             }, o.delay || 10);
5865         };
5866     };
5867
5868     var listen = function(element, ename, opt, fn, scope){
5869         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5870         fn = fn || o.fn; scope = scope || o.scope;
5871         var el = Roo.getDom(element);
5872         if(!el){
5873             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5874         }
5875         var h = function(e){
5876             e = Roo.EventObject.setEvent(e);
5877             var t;
5878             if(o.delegate){
5879                 t = e.getTarget(o.delegate, el);
5880                 if(!t){
5881                     return;
5882                 }
5883             }else{
5884                 t = e.target;
5885             }
5886             if(o.stopEvent === true){
5887                 e.stopEvent();
5888             }
5889             if(o.preventDefault === true){
5890                e.preventDefault();
5891             }
5892             if(o.stopPropagation === true){
5893                 e.stopPropagation();
5894             }
5895
5896             if(o.normalized === false){
5897                 e = e.browserEvent;
5898             }
5899
5900             fn.call(scope || el, e, t, o);
5901         };
5902         if(o.delay){
5903             h = createDelayed(h, o);
5904         }
5905         if(o.single){
5906             h = createSingle(h, el, ename, fn);
5907         }
5908         if(o.buffer){
5909             h = createBuffered(h, o);
5910         }
5911         fn._handlers = fn._handlers || [];
5912         fn._handlers.push([Roo.id(el), ename, h]);
5913
5914         E.on(el, ename, h);
5915         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5916             el.addEventListener("DOMMouseScroll", h, false);
5917             E.on(window, 'unload', function(){
5918                 el.removeEventListener("DOMMouseScroll", h, false);
5919             });
5920         }
5921         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5922             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5923         }
5924         return h;
5925     };
5926
5927     var stopListening = function(el, ename, fn){
5928         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5929         if(hds){
5930             for(var i = 0, len = hds.length; i < len; i++){
5931                 var h = hds[i];
5932                 if(h[0] == id && h[1] == ename){
5933                     hd = h[2];
5934                     hds.splice(i, 1);
5935                     break;
5936                 }
5937             }
5938         }
5939         E.un(el, ename, hd);
5940         el = Roo.getDom(el);
5941         if(ename == "mousewheel" && el.addEventListener){
5942             el.removeEventListener("DOMMouseScroll", hd, false);
5943         }
5944         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5945             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5946         }
5947     };
5948
5949     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5950     
5951     var pub = {
5952         
5953         
5954         /** 
5955          * Fix for doc tools
5956          * @scope Roo.EventManager
5957          */
5958         
5959         
5960         /** 
5961          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5962          * object with a Roo.EventObject
5963          * @param {Function} fn        The method the event invokes
5964          * @param {Object}   scope    An object that becomes the scope of the handler
5965          * @param {boolean}  override If true, the obj passed in becomes
5966          *                             the execution scope of the listener
5967          * @return {Function} The wrapped function
5968          * @deprecated
5969          */
5970         wrap : function(fn, scope, override){
5971             return function(e){
5972                 Roo.EventObject.setEvent(e);
5973                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5974             };
5975         },
5976         
5977         /**
5978      * Appends an event handler to an element (shorthand for addListener)
5979      * @param {String/HTMLElement}   element        The html element or id to assign the
5980      * @param {String}   eventName The type of event to listen for
5981      * @param {Function} handler The method the event invokes
5982      * @param {Object}   scope (optional) The scope in which to execute the handler
5983      * function. The handler function's "this" context.
5984      * @param {Object}   options (optional) An object containing handler configuration
5985      * properties. This may contain any of the following properties:<ul>
5986      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5987      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
5988      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
5989      * <li>preventDefault {Boolean} True to prevent the default action</li>
5990      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
5991      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
5992      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5993      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5994      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5995      * by the specified number of milliseconds. If the event fires again within that time, the original
5996      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5997      * </ul><br>
5998      * <p>
5999      * <b>Combining Options</b><br>
6000      * Using the options argument, it is possible to combine different types of listeners:<br>
6001      * <br>
6002      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6003      * Code:<pre><code>
6004 el.on('click', this.onClick, this, {
6005     single: true,
6006     delay: 100,
6007     stopEvent : true,
6008     forumId: 4
6009 });</code></pre>
6010      * <p>
6011      * <b>Attaching multiple handlers in 1 call</b><br>
6012       * The method also allows for a single argument to be passed which is a config object containing properties
6013      * which specify multiple handlers.
6014      * <p>
6015      * Code:<pre><code>
6016 el.on({
6017     'click' : {
6018         fn: this.onClick
6019         scope: this,
6020         delay: 100
6021     },
6022     'mouseover' : {
6023         fn: this.onMouseOver
6024         scope: this
6025     },
6026     'mouseout' : {
6027         fn: this.onMouseOut
6028         scope: this
6029     }
6030 });</code></pre>
6031      * <p>
6032      * Or a shorthand syntax:<br>
6033      * Code:<pre><code>
6034 el.on({
6035     'click' : this.onClick,
6036     'mouseover' : this.onMouseOver,
6037     'mouseout' : this.onMouseOut
6038     scope: this
6039 });</code></pre>
6040      */
6041         addListener : function(element, eventName, fn, scope, options){
6042             if(typeof eventName == "object"){
6043                 var o = eventName;
6044                 for(var e in o){
6045                     if(propRe.test(e)){
6046                         continue;
6047                     }
6048                     if(typeof o[e] == "function"){
6049                         // shared options
6050                         listen(element, e, o, o[e], o.scope);
6051                     }else{
6052                         // individual options
6053                         listen(element, e, o[e]);
6054                     }
6055                 }
6056                 return;
6057             }
6058             return listen(element, eventName, options, fn, scope);
6059         },
6060         
6061         /**
6062          * Removes an event handler
6063          *
6064          * @param {String/HTMLElement}   element        The id or html element to remove the 
6065          *                             event from
6066          * @param {String}   eventName     The type of event
6067          * @param {Function} fn
6068          * @return {Boolean} True if a listener was actually removed
6069          */
6070         removeListener : function(element, eventName, fn){
6071             return stopListening(element, eventName, fn);
6072         },
6073         
6074         /**
6075          * Fires when the document is ready (before onload and before images are loaded). Can be 
6076          * accessed shorthanded Roo.onReady().
6077          * @param {Function} fn        The method the event invokes
6078          * @param {Object}   scope    An  object that becomes the scope of the handler
6079          * @param {boolean}  options
6080          */
6081         onDocumentReady : function(fn, scope, options){
6082             if(docReadyState){ // if it already fired
6083                 docReadyEvent.addListener(fn, scope, options);
6084                 docReadyEvent.fire();
6085                 docReadyEvent.clearListeners();
6086                 return;
6087             }
6088             if(!docReadyEvent){
6089                 initDocReady();
6090             }
6091             docReadyEvent.addListener(fn, scope, options);
6092         },
6093         
6094         /**
6095          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6096          * @param {Function} fn        The method the event invokes
6097          * @param {Object}   scope    An object that becomes the scope of the handler
6098          * @param {boolean}  options
6099          */
6100         onWindowResize : function(fn, scope, options){
6101             if(!resizeEvent){
6102                 resizeEvent = new Roo.util.Event();
6103                 resizeTask = new Roo.util.DelayedTask(function(){
6104                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6105                 });
6106                 E.on(window, "resize", function(){
6107                     if(Roo.isIE){
6108                         resizeTask.delay(50);
6109                     }else{
6110                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6111                     }
6112                 });
6113             }
6114             resizeEvent.addListener(fn, scope, options);
6115         },
6116
6117         /**
6118          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6119          * @param {Function} fn        The method the event invokes
6120          * @param {Object}   scope    An object that becomes the scope of the handler
6121          * @param {boolean}  options
6122          */
6123         onTextResize : function(fn, scope, options){
6124             if(!textEvent){
6125                 textEvent = new Roo.util.Event();
6126                 var textEl = new Roo.Element(document.createElement('div'));
6127                 textEl.dom.className = 'x-text-resize';
6128                 textEl.dom.innerHTML = 'X';
6129                 textEl.appendTo(document.body);
6130                 textSize = textEl.dom.offsetHeight;
6131                 setInterval(function(){
6132                     if(textEl.dom.offsetHeight != textSize){
6133                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6134                     }
6135                 }, this.textResizeInterval);
6136             }
6137             textEvent.addListener(fn, scope, options);
6138         },
6139
6140         /**
6141          * Removes the passed window resize listener.
6142          * @param {Function} fn        The method the event invokes
6143          * @param {Object}   scope    The scope of handler
6144          */
6145         removeResizeListener : function(fn, scope){
6146             if(resizeEvent){
6147                 resizeEvent.removeListener(fn, scope);
6148             }
6149         },
6150
6151         // private
6152         fireResize : function(){
6153             if(resizeEvent){
6154                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6155             }   
6156         },
6157         /**
6158          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6159          */
6160         ieDeferSrc : false,
6161         /**
6162          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6163          */
6164         textResizeInterval : 50
6165     };
6166     
6167     /**
6168      * Fix for doc tools
6169      * @scopeAlias pub=Roo.EventManager
6170      */
6171     
6172      /**
6173      * Appends an event handler to an element (shorthand for addListener)
6174      * @param {String/HTMLElement}   element        The html element or id to assign the
6175      * @param {String}   eventName The type of event to listen for
6176      * @param {Function} handler The method the event invokes
6177      * @param {Object}   scope (optional) The scope in which to execute the handler
6178      * function. The handler function's "this" context.
6179      * @param {Object}   options (optional) An object containing handler configuration
6180      * properties. This may contain any of the following properties:<ul>
6181      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6182      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6183      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6184      * <li>preventDefault {Boolean} True to prevent the default action</li>
6185      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6186      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6187      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6188      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6189      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6190      * by the specified number of milliseconds. If the event fires again within that time, the original
6191      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6192      * </ul><br>
6193      * <p>
6194      * <b>Combining Options</b><br>
6195      * Using the options argument, it is possible to combine different types of listeners:<br>
6196      * <br>
6197      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6198      * Code:<pre><code>
6199 el.on('click', this.onClick, this, {
6200     single: true,
6201     delay: 100,
6202     stopEvent : true,
6203     forumId: 4
6204 });</code></pre>
6205      * <p>
6206      * <b>Attaching multiple handlers in 1 call</b><br>
6207       * The method also allows for a single argument to be passed which is a config object containing properties
6208      * which specify multiple handlers.
6209      * <p>
6210      * Code:<pre><code>
6211 el.on({
6212     'click' : {
6213         fn: this.onClick
6214         scope: this,
6215         delay: 100
6216     },
6217     'mouseover' : {
6218         fn: this.onMouseOver
6219         scope: this
6220     },
6221     'mouseout' : {
6222         fn: this.onMouseOut
6223         scope: this
6224     }
6225 });</code></pre>
6226      * <p>
6227      * Or a shorthand syntax:<br>
6228      * Code:<pre><code>
6229 el.on({
6230     'click' : this.onClick,
6231     'mouseover' : this.onMouseOver,
6232     'mouseout' : this.onMouseOut
6233     scope: this
6234 });</code></pre>
6235      */
6236     pub.on = pub.addListener;
6237     pub.un = pub.removeListener;
6238
6239     pub.stoppedMouseDownEvent = new Roo.util.Event();
6240     return pub;
6241 }();
6242 /**
6243   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6244   * @param {Function} fn        The method the event invokes
6245   * @param {Object}   scope    An  object that becomes the scope of the handler
6246   * @param {boolean}  override If true, the obj passed in becomes
6247   *                             the execution scope of the listener
6248   * @member Roo
6249   * @method onReady
6250  */
6251 Roo.onReady = Roo.EventManager.onDocumentReady;
6252
6253 Roo.onReady(function(){
6254     var bd = Roo.get(document.body);
6255     if(!bd){ return; }
6256
6257     var cls = [
6258             Roo.isIE ? "roo-ie"
6259             : Roo.isGecko ? "roo-gecko"
6260             : Roo.isOpera ? "roo-opera"
6261             : Roo.isSafari ? "roo-safari" : ""];
6262
6263     if(Roo.isMac){
6264         cls.push("roo-mac");
6265     }
6266     if(Roo.isLinux){
6267         cls.push("roo-linux");
6268     }
6269     if(Roo.isBorderBox){
6270         cls.push('roo-border-box');
6271     }
6272     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6273         var p = bd.dom.parentNode;
6274         if(p){
6275             p.className += ' roo-strict';
6276         }
6277     }
6278     bd.addClass(cls.join(' '));
6279 });
6280
6281 /**
6282  * @class Roo.EventObject
6283  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6284  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6285  * Example:
6286  * <pre><code>
6287  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6288     e.preventDefault();
6289     var target = e.getTarget();
6290     ...
6291  }
6292  var myDiv = Roo.get("myDiv");
6293  myDiv.on("click", handleClick);
6294  //or
6295  Roo.EventManager.on("myDiv", 'click', handleClick);
6296  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6297  </code></pre>
6298  * @singleton
6299  */
6300 Roo.EventObject = function(){
6301     
6302     var E = Roo.lib.Event;
6303     
6304     // safari keypress events for special keys return bad keycodes
6305     var safariKeys = {
6306         63234 : 37, // left
6307         63235 : 39, // right
6308         63232 : 38, // up
6309         63233 : 40, // down
6310         63276 : 33, // page up
6311         63277 : 34, // page down
6312         63272 : 46, // delete
6313         63273 : 36, // home
6314         63275 : 35  // end
6315     };
6316
6317     // normalize button clicks
6318     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6319                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6320
6321     Roo.EventObjectImpl = function(e){
6322         if(e){
6323             this.setEvent(e.browserEvent || e);
6324         }
6325     };
6326     Roo.EventObjectImpl.prototype = {
6327         /**
6328          * Used to fix doc tools.
6329          * @scope Roo.EventObject.prototype
6330          */
6331             
6332
6333         
6334         
6335         /** The normal browser event */
6336         browserEvent : null,
6337         /** The button pressed in a mouse event */
6338         button : -1,
6339         /** True if the shift key was down during the event */
6340         shiftKey : false,
6341         /** True if the control key was down during the event */
6342         ctrlKey : false,
6343         /** True if the alt key was down during the event */
6344         altKey : false,
6345
6346         /** Key constant 
6347         * @type Number */
6348         BACKSPACE : 8,
6349         /** Key constant 
6350         * @type Number */
6351         TAB : 9,
6352         /** Key constant 
6353         * @type Number */
6354         RETURN : 13,
6355         /** Key constant 
6356         * @type Number */
6357         ENTER : 13,
6358         /** Key constant 
6359         * @type Number */
6360         SHIFT : 16,
6361         /** Key constant 
6362         * @type Number */
6363         CONTROL : 17,
6364         /** Key constant 
6365         * @type Number */
6366         ESC : 27,
6367         /** Key constant 
6368         * @type Number */
6369         SPACE : 32,
6370         /** Key constant 
6371         * @type Number */
6372         PAGEUP : 33,
6373         /** Key constant 
6374         * @type Number */
6375         PAGEDOWN : 34,
6376         /** Key constant 
6377         * @type Number */
6378         END : 35,
6379         /** Key constant 
6380         * @type Number */
6381         HOME : 36,
6382         /** Key constant 
6383         * @type Number */
6384         LEFT : 37,
6385         /** Key constant 
6386         * @type Number */
6387         UP : 38,
6388         /** Key constant 
6389         * @type Number */
6390         RIGHT : 39,
6391         /** Key constant 
6392         * @type Number */
6393         DOWN : 40,
6394         /** Key constant 
6395         * @type Number */
6396         DELETE : 46,
6397         /** Key constant 
6398         * @type Number */
6399         F5 : 116,
6400
6401            /** @private */
6402         setEvent : function(e){
6403             if(e == this || (e && e.browserEvent)){ // already wrapped
6404                 return e;
6405             }
6406             this.browserEvent = e;
6407             if(e){
6408                 // normalize buttons
6409                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6410                 if(e.type == 'click' && this.button == -1){
6411                     this.button = 0;
6412                 }
6413                 this.type = e.type;
6414                 this.shiftKey = e.shiftKey;
6415                 // mac metaKey behaves like ctrlKey
6416                 this.ctrlKey = e.ctrlKey || e.metaKey;
6417                 this.altKey = e.altKey;
6418                 // in getKey these will be normalized for the mac
6419                 this.keyCode = e.keyCode;
6420                 // keyup warnings on firefox.
6421                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6422                 // cache the target for the delayed and or buffered events
6423                 this.target = E.getTarget(e);
6424                 // same for XY
6425                 this.xy = E.getXY(e);
6426             }else{
6427                 this.button = -1;
6428                 this.shiftKey = false;
6429                 this.ctrlKey = false;
6430                 this.altKey = false;
6431                 this.keyCode = 0;
6432                 this.charCode =0;
6433                 this.target = null;
6434                 this.xy = [0, 0];
6435             }
6436             return this;
6437         },
6438
6439         /**
6440          * Stop the event (preventDefault and stopPropagation)
6441          */
6442         stopEvent : function(){
6443             if(this.browserEvent){
6444                 if(this.browserEvent.type == 'mousedown'){
6445                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6446                 }
6447                 E.stopEvent(this.browserEvent);
6448             }
6449         },
6450
6451         /**
6452          * Prevents the browsers default handling of the event.
6453          */
6454         preventDefault : function(){
6455             if(this.browserEvent){
6456                 E.preventDefault(this.browserEvent);
6457             }
6458         },
6459
6460         /** @private */
6461         isNavKeyPress : function(){
6462             var k = this.keyCode;
6463             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6464             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6465         },
6466
6467         isSpecialKey : function(){
6468             var k = this.keyCode;
6469             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6470             (k == 16) || (k == 17) ||
6471             (k >= 18 && k <= 20) ||
6472             (k >= 33 && k <= 35) ||
6473             (k >= 36 && k <= 39) ||
6474             (k >= 44 && k <= 45);
6475         },
6476         /**
6477          * Cancels bubbling of the event.
6478          */
6479         stopPropagation : function(){
6480             if(this.browserEvent){
6481                 if(this.type == 'mousedown'){
6482                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6483                 }
6484                 E.stopPropagation(this.browserEvent);
6485             }
6486         },
6487
6488         /**
6489          * Gets the key code for the event.
6490          * @return {Number}
6491          */
6492         getCharCode : function(){
6493             return this.charCode || this.keyCode;
6494         },
6495
6496         /**
6497          * Returns a normalized keyCode for the event.
6498          * @return {Number} The key code
6499          */
6500         getKey : function(){
6501             var k = this.keyCode || this.charCode;
6502             return Roo.isSafari ? (safariKeys[k] || k) : k;
6503         },
6504
6505         /**
6506          * Gets the x coordinate of the event.
6507          * @return {Number}
6508          */
6509         getPageX : function(){
6510             return this.xy[0];
6511         },
6512
6513         /**
6514          * Gets the y coordinate of the event.
6515          * @return {Number}
6516          */
6517         getPageY : function(){
6518             return this.xy[1];
6519         },
6520
6521         /**
6522          * Gets the time of the event.
6523          * @return {Number}
6524          */
6525         getTime : function(){
6526             if(this.browserEvent){
6527                 return E.getTime(this.browserEvent);
6528             }
6529             return null;
6530         },
6531
6532         /**
6533          * Gets the page coordinates of the event.
6534          * @return {Array} The xy values like [x, y]
6535          */
6536         getXY : function(){
6537             return this.xy;
6538         },
6539
6540         /**
6541          * Gets the target for the event.
6542          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6543          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6544                 search as a number or element (defaults to 10 || document.body)
6545          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6546          * @return {HTMLelement}
6547          */
6548         getTarget : function(selector, maxDepth, returnEl){
6549             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6550         },
6551         /**
6552          * Gets the related target.
6553          * @return {HTMLElement}
6554          */
6555         getRelatedTarget : function(){
6556             if(this.browserEvent){
6557                 return E.getRelatedTarget(this.browserEvent);
6558             }
6559             return null;
6560         },
6561
6562         /**
6563          * Normalizes mouse wheel delta across browsers
6564          * @return {Number} The delta
6565          */
6566         getWheelDelta : function(){
6567             var e = this.browserEvent;
6568             var delta = 0;
6569             if(e.wheelDelta){ /* IE/Opera. */
6570                 delta = e.wheelDelta/120;
6571             }else if(e.detail){ /* Mozilla case. */
6572                 delta = -e.detail/3;
6573             }
6574             return delta;
6575         },
6576
6577         /**
6578          * Returns true if the control, meta, shift or alt key was pressed during this event.
6579          * @return {Boolean}
6580          */
6581         hasModifier : function(){
6582             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6583         },
6584
6585         /**
6586          * Returns true if the target of this event equals el or is a child of el
6587          * @param {String/HTMLElement/Element} el
6588          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6589          * @return {Boolean}
6590          */
6591         within : function(el, related){
6592             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6593             return t && Roo.fly(el).contains(t);
6594         },
6595
6596         getPoint : function(){
6597             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6598         }
6599     };
6600
6601     return new Roo.EventObjectImpl();
6602 }();
6603             
6604     /*
6605  * Based on:
6606  * Ext JS Library 1.1.1
6607  * Copyright(c) 2006-2007, Ext JS, LLC.
6608  *
6609  * Originally Released Under LGPL - original licence link has changed is not relivant.
6610  *
6611  * Fork - LGPL
6612  * <script type="text/javascript">
6613  */
6614
6615  
6616 // was in Composite Element!??!?!
6617  
6618 (function(){
6619     var D = Roo.lib.Dom;
6620     var E = Roo.lib.Event;
6621     var A = Roo.lib.Anim;
6622
6623     // local style camelizing for speed
6624     var propCache = {};
6625     var camelRe = /(-[a-z])/gi;
6626     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6627     var view = document.defaultView;
6628
6629 /**
6630  * @class Roo.Element
6631  * Represents an Element in the DOM.<br><br>
6632  * Usage:<br>
6633 <pre><code>
6634 var el = Roo.get("my-div");
6635
6636 // or with getEl
6637 var el = getEl("my-div");
6638
6639 // or with a DOM element
6640 var el = Roo.get(myDivElement);
6641 </code></pre>
6642  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6643  * each call instead of constructing a new one.<br><br>
6644  * <b>Animations</b><br />
6645  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6646  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6647 <pre>
6648 Option    Default   Description
6649 --------- --------  ---------------------------------------------
6650 duration  .35       The duration of the animation in seconds
6651 easing    easeOut   The YUI easing method
6652 callback  none      A function to execute when the anim completes
6653 scope     this      The scope (this) of the callback function
6654 </pre>
6655 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6656 * manipulate the animation. Here's an example:
6657 <pre><code>
6658 var el = Roo.get("my-div");
6659
6660 // no animation
6661 el.setWidth(100);
6662
6663 // default animation
6664 el.setWidth(100, true);
6665
6666 // animation with some options set
6667 el.setWidth(100, {
6668     duration: 1,
6669     callback: this.foo,
6670     scope: this
6671 });
6672
6673 // using the "anim" property to get the Anim object
6674 var opt = {
6675     duration: 1,
6676     callback: this.foo,
6677     scope: this
6678 };
6679 el.setWidth(100, opt);
6680 ...
6681 if(opt.anim.isAnimated()){
6682     opt.anim.stop();
6683 }
6684 </code></pre>
6685 * <b> Composite (Collections of) Elements</b><br />
6686  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6687  * @constructor Create a new Element directly.
6688  * @param {String/HTMLElement} element
6689  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6690  */
6691     Roo.Element = function(element, forceNew){
6692         var dom = typeof element == "string" ?
6693                 document.getElementById(element) : element;
6694         if(!dom){ // invalid id/element
6695             return null;
6696         }
6697         var id = dom.id;
6698         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6699             return Roo.Element.cache[id];
6700         }
6701
6702         /**
6703          * The DOM element
6704          * @type HTMLElement
6705          */
6706         this.dom = dom;
6707
6708         /**
6709          * The DOM element ID
6710          * @type String
6711          */
6712         this.id = id || Roo.id(dom);
6713     };
6714
6715     var El = Roo.Element;
6716
6717     El.prototype = {
6718         /**
6719          * The element's default display mode  (defaults to "")
6720          * @type String
6721          */
6722         originalDisplay : "",
6723
6724         visibilityMode : 1,
6725         /**
6726          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6727          * @type String
6728          */
6729         defaultUnit : "px",
6730         /**
6731          * Sets the element's visibility mode. When setVisible() is called it
6732          * will use this to determine whether to set the visibility or the display property.
6733          * @param visMode Element.VISIBILITY or Element.DISPLAY
6734          * @return {Roo.Element} this
6735          */
6736         setVisibilityMode : function(visMode){
6737             this.visibilityMode = visMode;
6738             return this;
6739         },
6740         /**
6741          * Convenience method for setVisibilityMode(Element.DISPLAY)
6742          * @param {String} display (optional) What to set display to when visible
6743          * @return {Roo.Element} this
6744          */
6745         enableDisplayMode : function(display){
6746             this.setVisibilityMode(El.DISPLAY);
6747             if(typeof display != "undefined") this.originalDisplay = display;
6748             return this;
6749         },
6750
6751         /**
6752          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6753          * @param {String} selector The simple selector to test
6754          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6755                 search as a number or element (defaults to 10 || document.body)
6756          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6757          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6758          */
6759         findParent : function(simpleSelector, maxDepth, returnEl){
6760             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6761             maxDepth = maxDepth || 50;
6762             if(typeof maxDepth != "number"){
6763                 stopEl = Roo.getDom(maxDepth);
6764                 maxDepth = 10;
6765             }
6766             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6767                 if(dq.is(p, simpleSelector)){
6768                     return returnEl ? Roo.get(p) : p;
6769                 }
6770                 depth++;
6771                 p = p.parentNode;
6772             }
6773             return null;
6774         },
6775
6776
6777         /**
6778          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6779          * @param {String} selector The simple selector to test
6780          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6781                 search as a number or element (defaults to 10 || document.body)
6782          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6783          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6784          */
6785         findParentNode : function(simpleSelector, maxDepth, returnEl){
6786             var p = Roo.fly(this.dom.parentNode, '_internal');
6787             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6788         },
6789
6790         /**
6791          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6792          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6793          * @param {String} selector The simple selector to test
6794          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6795                 search as a number or element (defaults to 10 || document.body)
6796          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6797          */
6798         up : function(simpleSelector, maxDepth){
6799             return this.findParentNode(simpleSelector, maxDepth, true);
6800         },
6801
6802
6803
6804         /**
6805          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6806          * @param {String} selector The simple selector to test
6807          * @return {Boolean} True if this element matches the selector, else false
6808          */
6809         is : function(simpleSelector){
6810             return Roo.DomQuery.is(this.dom, simpleSelector);
6811         },
6812
6813         /**
6814          * Perform animation on this element.
6815          * @param {Object} args The YUI animation control args
6816          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6817          * @param {Function} onComplete (optional) Function to call when animation completes
6818          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6819          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6820          * @return {Roo.Element} this
6821          */
6822         animate : function(args, duration, onComplete, easing, animType){
6823             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6824             return this;
6825         },
6826
6827         /*
6828          * @private Internal animation call
6829          */
6830         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6831             animType = animType || 'run';
6832             opt = opt || {};
6833             var anim = Roo.lib.Anim[animType](
6834                 this.dom, args,
6835                 (opt.duration || defaultDur) || .35,
6836                 (opt.easing || defaultEase) || 'easeOut',
6837                 function(){
6838                     Roo.callback(cb, this);
6839                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6840                 },
6841                 this
6842             );
6843             opt.anim = anim;
6844             return anim;
6845         },
6846
6847         // private legacy anim prep
6848         preanim : function(a, i){
6849             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6850         },
6851
6852         /**
6853          * Removes worthless text nodes
6854          * @param {Boolean} forceReclean (optional) By default the element
6855          * keeps track if it has been cleaned already so
6856          * you can call this over and over. However, if you update the element and
6857          * need to force a reclean, you can pass true.
6858          */
6859         clean : function(forceReclean){
6860             if(this.isCleaned && forceReclean !== true){
6861                 return this;
6862             }
6863             var ns = /\S/;
6864             var d = this.dom, n = d.firstChild, ni = -1;
6865             while(n){
6866                 var nx = n.nextSibling;
6867                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6868                     d.removeChild(n);
6869                 }else{
6870                     n.nodeIndex = ++ni;
6871                 }
6872                 n = nx;
6873             }
6874             this.isCleaned = true;
6875             return this;
6876         },
6877
6878         // private
6879         calcOffsetsTo : function(el){
6880             el = Roo.get(el);
6881             var d = el.dom;
6882             var restorePos = false;
6883             if(el.getStyle('position') == 'static'){
6884                 el.position('relative');
6885                 restorePos = true;
6886             }
6887             var x = 0, y =0;
6888             var op = this.dom;
6889             while(op && op != d && op.tagName != 'HTML'){
6890                 x+= op.offsetLeft;
6891                 y+= op.offsetTop;
6892                 op = op.offsetParent;
6893             }
6894             if(restorePos){
6895                 el.position('static');
6896             }
6897             return [x, y];
6898         },
6899
6900         /**
6901          * Scrolls this element into view within the passed container.
6902          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6903          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6904          * @return {Roo.Element} this
6905          */
6906         scrollIntoView : function(container, hscroll){
6907             var c = Roo.getDom(container) || document.body;
6908             var el = this.dom;
6909
6910             var o = this.calcOffsetsTo(c),
6911                 l = o[0],
6912                 t = o[1],
6913                 b = t+el.offsetHeight,
6914                 r = l+el.offsetWidth;
6915
6916             var ch = c.clientHeight;
6917             var ct = parseInt(c.scrollTop, 10);
6918             var cl = parseInt(c.scrollLeft, 10);
6919             var cb = ct + ch;
6920             var cr = cl + c.clientWidth;
6921
6922             if(t < ct){
6923                 c.scrollTop = t;
6924             }else if(b > cb){
6925                 c.scrollTop = b-ch;
6926             }
6927
6928             if(hscroll !== false){
6929                 if(l < cl){
6930                     c.scrollLeft = l;
6931                 }else if(r > cr){
6932                     c.scrollLeft = r-c.clientWidth;
6933                 }
6934             }
6935             return this;
6936         },
6937
6938         // private
6939         scrollChildIntoView : function(child, hscroll){
6940             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6941         },
6942
6943         /**
6944          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6945          * the new height may not be available immediately.
6946          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6947          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6948          * @param {Function} onComplete (optional) Function to call when animation completes
6949          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6950          * @return {Roo.Element} this
6951          */
6952         autoHeight : function(animate, duration, onComplete, easing){
6953             var oldHeight = this.getHeight();
6954             this.clip();
6955             this.setHeight(1); // force clipping
6956             setTimeout(function(){
6957                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6958                 if(!animate){
6959                     this.setHeight(height);
6960                     this.unclip();
6961                     if(typeof onComplete == "function"){
6962                         onComplete();
6963                     }
6964                 }else{
6965                     this.setHeight(oldHeight); // restore original height
6966                     this.setHeight(height, animate, duration, function(){
6967                         this.unclip();
6968                         if(typeof onComplete == "function") onComplete();
6969                     }.createDelegate(this), easing);
6970                 }
6971             }.createDelegate(this), 0);
6972             return this;
6973         },
6974
6975         /**
6976          * Returns true if this element is an ancestor of the passed element
6977          * @param {HTMLElement/String} el The element to check
6978          * @return {Boolean} True if this element is an ancestor of el, else false
6979          */
6980         contains : function(el){
6981             if(!el){return false;}
6982             return D.isAncestor(this.dom, el.dom ? el.dom : el);
6983         },
6984
6985         /**
6986          * Checks whether the element is currently visible using both visibility and display properties.
6987          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
6988          * @return {Boolean} True if the element is currently visible, else false
6989          */
6990         isVisible : function(deep) {
6991             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
6992             if(deep !== true || !vis){
6993                 return vis;
6994             }
6995             var p = this.dom.parentNode;
6996             while(p && p.tagName.toLowerCase() != "body"){
6997                 if(!Roo.fly(p, '_isVisible').isVisible()){
6998                     return false;
6999                 }
7000                 p = p.parentNode;
7001             }
7002             return true;
7003         },
7004
7005         /**
7006          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7007          * @param {String} selector The CSS selector
7008          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7009          * @return {CompositeElement/CompositeElementLite} The composite element
7010          */
7011         select : function(selector, unique){
7012             return El.select(selector, unique, this.dom);
7013         },
7014
7015         /**
7016          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7017          * @param {String} selector The CSS selector
7018          * @return {Array} An array of the matched nodes
7019          */
7020         query : function(selector, unique){
7021             return Roo.DomQuery.select(selector, this.dom);
7022         },
7023
7024         /**
7025          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7026          * @param {String} selector The CSS selector
7027          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7028          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7029          */
7030         child : function(selector, returnDom){
7031             var n = Roo.DomQuery.selectNode(selector, this.dom);
7032             return returnDom ? n : Roo.get(n);
7033         },
7034
7035         /**
7036          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7037          * @param {String} selector The CSS selector
7038          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7039          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7040          */
7041         down : function(selector, returnDom){
7042             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7043             return returnDom ? n : Roo.get(n);
7044         },
7045
7046         /**
7047          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7048          * @param {String} group The group the DD object is member of
7049          * @param {Object} config The DD config object
7050          * @param {Object} overrides An object containing methods to override/implement on the DD object
7051          * @return {Roo.dd.DD} The DD object
7052          */
7053         initDD : function(group, config, overrides){
7054             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7055             return Roo.apply(dd, overrides);
7056         },
7057
7058         /**
7059          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7060          * @param {String} group The group the DDProxy object is member of
7061          * @param {Object} config The DDProxy config object
7062          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7063          * @return {Roo.dd.DDProxy} The DDProxy object
7064          */
7065         initDDProxy : function(group, config, overrides){
7066             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7067             return Roo.apply(dd, overrides);
7068         },
7069
7070         /**
7071          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7072          * @param {String} group The group the DDTarget object is member of
7073          * @param {Object} config The DDTarget config object
7074          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7075          * @return {Roo.dd.DDTarget} The DDTarget object
7076          */
7077         initDDTarget : function(group, config, overrides){
7078             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7079             return Roo.apply(dd, overrides);
7080         },
7081
7082         /**
7083          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7084          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7085          * @param {Boolean} visible Whether the element is visible
7086          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7087          * @return {Roo.Element} this
7088          */
7089          setVisible : function(visible, animate){
7090             if(!animate || !A){
7091                 if(this.visibilityMode == El.DISPLAY){
7092                     this.setDisplayed(visible);
7093                 }else{
7094                     this.fixDisplay();
7095                     this.dom.style.visibility = visible ? "visible" : "hidden";
7096                 }
7097             }else{
7098                 // closure for composites
7099                 var dom = this.dom;
7100                 var visMode = this.visibilityMode;
7101                 if(visible){
7102                     this.setOpacity(.01);
7103                     this.setVisible(true);
7104                 }
7105                 this.anim({opacity: { to: (visible?1:0) }},
7106                       this.preanim(arguments, 1),
7107                       null, .35, 'easeIn', function(){
7108                          if(!visible){
7109                              if(visMode == El.DISPLAY){
7110                                  dom.style.display = "none";
7111                              }else{
7112                                  dom.style.visibility = "hidden";
7113                              }
7114                              Roo.get(dom).setOpacity(1);
7115                          }
7116                      });
7117             }
7118             return this;
7119         },
7120
7121         /**
7122          * Returns true if display is not "none"
7123          * @return {Boolean}
7124          */
7125         isDisplayed : function() {
7126             return this.getStyle("display") != "none";
7127         },
7128
7129         /**
7130          * Toggles the element's visibility or display, depending on visibility mode.
7131          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7132          * @return {Roo.Element} this
7133          */
7134         toggle : function(animate){
7135             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7136             return this;
7137         },
7138
7139         /**
7140          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7141          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7142          * @return {Roo.Element} this
7143          */
7144         setDisplayed : function(value) {
7145             if(typeof value == "boolean"){
7146                value = value ? this.originalDisplay : "none";
7147             }
7148             this.setStyle("display", value);
7149             return this;
7150         },
7151
7152         /**
7153          * Tries to focus the element. Any exceptions are caught and ignored.
7154          * @return {Roo.Element} this
7155          */
7156         focus : function() {
7157             try{
7158                 this.dom.focus();
7159             }catch(e){}
7160             return this;
7161         },
7162
7163         /**
7164          * Tries to blur the element. Any exceptions are caught and ignored.
7165          * @return {Roo.Element} this
7166          */
7167         blur : function() {
7168             try{
7169                 this.dom.blur();
7170             }catch(e){}
7171             return this;
7172         },
7173
7174         /**
7175          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7176          * @param {String/Array} className The CSS class to add, or an array of classes
7177          * @return {Roo.Element} this
7178          */
7179         addClass : function(className){
7180             if(className instanceof Array){
7181                 for(var i = 0, len = className.length; i < len; i++) {
7182                     this.addClass(className[i]);
7183                 }
7184             }else{
7185                 if(className && !this.hasClass(className)){
7186                     this.dom.className = this.dom.className + " " + className;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         /**
7193          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7194          * @param {String/Array} className The CSS class to add, or an array of classes
7195          * @return {Roo.Element} this
7196          */
7197         radioClass : function(className){
7198             var siblings = this.dom.parentNode.childNodes;
7199             for(var i = 0; i < siblings.length; i++) {
7200                 var s = siblings[i];
7201                 if(s.nodeType == 1){
7202                     Roo.get(s).removeClass(className);
7203                 }
7204             }
7205             this.addClass(className);
7206             return this;
7207         },
7208
7209         /**
7210          * Removes one or more CSS classes from the element.
7211          * @param {String/Array} className The CSS class to remove, or an array of classes
7212          * @return {Roo.Element} this
7213          */
7214         removeClass : function(className){
7215             if(!className || !this.dom.className){
7216                 return this;
7217             }
7218             if(className instanceof Array){
7219                 for(var i = 0, len = className.length; i < len; i++) {
7220                     this.removeClass(className[i]);
7221                 }
7222             }else{
7223                 if(this.hasClass(className)){
7224                     var re = this.classReCache[className];
7225                     if (!re) {
7226                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7227                        this.classReCache[className] = re;
7228                     }
7229                     this.dom.className =
7230                         this.dom.className.replace(re, " ");
7231                 }
7232             }
7233             return this;
7234         },
7235
7236         // private
7237         classReCache: {},
7238
7239         /**
7240          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7241          * @param {String} className The CSS class to toggle
7242          * @return {Roo.Element} this
7243          */
7244         toggleClass : function(className){
7245             if(this.hasClass(className)){
7246                 this.removeClass(className);
7247             }else{
7248                 this.addClass(className);
7249             }
7250             return this;
7251         },
7252
7253         /**
7254          * Checks if the specified CSS class exists on this element's DOM node.
7255          * @param {String} className The CSS class to check for
7256          * @return {Boolean} True if the class exists, else false
7257          */
7258         hasClass : function(className){
7259             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7260         },
7261
7262         /**
7263          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7264          * @param {String} oldClassName The CSS class to replace
7265          * @param {String} newClassName The replacement CSS class
7266          * @return {Roo.Element} this
7267          */
7268         replaceClass : function(oldClassName, newClassName){
7269             this.removeClass(oldClassName);
7270             this.addClass(newClassName);
7271             return this;
7272         },
7273
7274         /**
7275          * Returns an object with properties matching the styles requested.
7276          * For example, el.getStyles('color', 'font-size', 'width') might return
7277          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7278          * @param {String} style1 A style name
7279          * @param {String} style2 A style name
7280          * @param {String} etc.
7281          * @return {Object} The style object
7282          */
7283         getStyles : function(){
7284             var a = arguments, len = a.length, r = {};
7285             for(var i = 0; i < len; i++){
7286                 r[a[i]] = this.getStyle(a[i]);
7287             }
7288             return r;
7289         },
7290
7291         /**
7292          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7293          * @param {String} property The style property whose value is returned.
7294          * @return {String} The current value of the style property for this element.
7295          */
7296         getStyle : function(){
7297             return view && view.getComputedStyle ?
7298                 function(prop){
7299                     var el = this.dom, v, cs, camel;
7300                     if(prop == 'float'){
7301                         prop = "cssFloat";
7302                     }
7303                     if(el.style && (v = el.style[prop])){
7304                         return v;
7305                     }
7306                     if(cs = view.getComputedStyle(el, "")){
7307                         if(!(camel = propCache[prop])){
7308                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7309                         }
7310                         return cs[camel];
7311                     }
7312                     return null;
7313                 } :
7314                 function(prop){
7315                     var el = this.dom, v, cs, camel;
7316                     if(prop == 'opacity'){
7317                         if(typeof el.style.filter == 'string'){
7318                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7319                             if(m){
7320                                 var fv = parseFloat(m[1]);
7321                                 if(!isNaN(fv)){
7322                                     return fv ? fv / 100 : 0;
7323                                 }
7324                             }
7325                         }
7326                         return 1;
7327                     }else if(prop == 'float'){
7328                         prop = "styleFloat";
7329                     }
7330                     if(!(camel = propCache[prop])){
7331                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7332                     }
7333                     if(v = el.style[camel]){
7334                         return v;
7335                     }
7336                     if(cs = el.currentStyle){
7337                         return cs[camel];
7338                     }
7339                     return null;
7340                 };
7341         }(),
7342
7343         /**
7344          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7345          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7346          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7347          * @return {Roo.Element} this
7348          */
7349         setStyle : function(prop, value){
7350             if(typeof prop == "string"){
7351                 var camel;
7352                 if(!(camel = propCache[prop])){
7353                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7354                 }
7355                 if(camel == 'opacity') {
7356                     this.setOpacity(value);
7357                 }else{
7358                     this.dom.style[camel] = value;
7359                 }
7360             }else{
7361                 for(var style in prop){
7362                     if(typeof prop[style] != "function"){
7363                        this.setStyle(style, prop[style]);
7364                     }
7365                 }
7366             }
7367             return this;
7368         },
7369
7370         /**
7371          * More flexible version of {@link #setStyle} for setting style properties.
7372          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7373          * a function which returns such a specification.
7374          * @return {Roo.Element} this
7375          */
7376         applyStyles : function(style){
7377             Roo.DomHelper.applyStyles(this.dom, style);
7378             return this;
7379         },
7380
7381         /**
7382           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7383           * @return {Number} The X position of the element
7384           */
7385         getX : function(){
7386             return D.getX(this.dom);
7387         },
7388
7389         /**
7390           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7391           * @return {Number} The Y position of the element
7392           */
7393         getY : function(){
7394             return D.getY(this.dom);
7395         },
7396
7397         /**
7398           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7399           * @return {Array} The XY position of the element
7400           */
7401         getXY : function(){
7402             return D.getXY(this.dom);
7403         },
7404
7405         /**
7406          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7407          * @param {Number} The X position of the element
7408          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7409          * @return {Roo.Element} this
7410          */
7411         setX : function(x, animate){
7412             if(!animate || !A){
7413                 D.setX(this.dom, x);
7414             }else{
7415                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7416             }
7417             return this;
7418         },
7419
7420         /**
7421          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7422          * @param {Number} The Y position of the element
7423          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7424          * @return {Roo.Element} this
7425          */
7426         setY : function(y, animate){
7427             if(!animate || !A){
7428                 D.setY(this.dom, y);
7429             }else{
7430                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7437          * @param {String} left The left CSS property value
7438          * @return {Roo.Element} this
7439          */
7440         setLeft : function(left){
7441             this.setStyle("left", this.addUnits(left));
7442             return this;
7443         },
7444
7445         /**
7446          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7447          * @param {String} top The top CSS property value
7448          * @return {Roo.Element} this
7449          */
7450         setTop : function(top){
7451             this.setStyle("top", this.addUnits(top));
7452             return this;
7453         },
7454
7455         /**
7456          * Sets the element's CSS right style.
7457          * @param {String} right The right CSS property value
7458          * @return {Roo.Element} this
7459          */
7460         setRight : function(right){
7461             this.setStyle("right", this.addUnits(right));
7462             return this;
7463         },
7464
7465         /**
7466          * Sets the element's CSS bottom style.
7467          * @param {String} bottom The bottom CSS property value
7468          * @return {Roo.Element} this
7469          */
7470         setBottom : function(bottom){
7471             this.setStyle("bottom", this.addUnits(bottom));
7472             return this;
7473         },
7474
7475         /**
7476          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7477          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7478          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7479          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7480          * @return {Roo.Element} this
7481          */
7482         setXY : function(pos, animate){
7483             if(!animate || !A){
7484                 D.setXY(this.dom, pos);
7485             }else{
7486                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7487             }
7488             return this;
7489         },
7490
7491         /**
7492          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7493          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7494          * @param {Number} x X value for new position (coordinates are page-based)
7495          * @param {Number} y Y value for new position (coordinates are page-based)
7496          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497          * @return {Roo.Element} this
7498          */
7499         setLocation : function(x, y, animate){
7500             this.setXY([x, y], this.preanim(arguments, 2));
7501             return this;
7502         },
7503
7504         /**
7505          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7506          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7507          * @param {Number} x X value for new position (coordinates are page-based)
7508          * @param {Number} y Y value for new position (coordinates are page-based)
7509          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7510          * @return {Roo.Element} this
7511          */
7512         moveTo : function(x, y, animate){
7513             this.setXY([x, y], this.preanim(arguments, 2));
7514             return this;
7515         },
7516
7517         /**
7518          * Returns the region of the given element.
7519          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7520          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7521          */
7522         getRegion : function(){
7523             return D.getRegion(this.dom);
7524         },
7525
7526         /**
7527          * Returns the offset height of the element
7528          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7529          * @return {Number} The element's height
7530          */
7531         getHeight : function(contentHeight){
7532             var h = this.dom.offsetHeight || 0;
7533             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7534         },
7535
7536         /**
7537          * Returns the offset width of the element
7538          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7539          * @return {Number} The element's width
7540          */
7541         getWidth : function(contentWidth){
7542             var w = this.dom.offsetWidth || 0;
7543             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7544         },
7545
7546         /**
7547          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7548          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7549          * if a height has not been set using CSS.
7550          * @return {Number}
7551          */
7552         getComputedHeight : function(){
7553             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7554             if(!h){
7555                 h = parseInt(this.getStyle('height'), 10) || 0;
7556                 if(!this.isBorderBox()){
7557                     h += this.getFrameWidth('tb');
7558                 }
7559             }
7560             return h;
7561         },
7562
7563         /**
7564          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7565          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7566          * if a width has not been set using CSS.
7567          * @return {Number}
7568          */
7569         getComputedWidth : function(){
7570             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7571             if(!w){
7572                 w = parseInt(this.getStyle('width'), 10) || 0;
7573                 if(!this.isBorderBox()){
7574                     w += this.getFrameWidth('lr');
7575                 }
7576             }
7577             return w;
7578         },
7579
7580         /**
7581          * Returns the size of the element.
7582          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7583          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7584          */
7585         getSize : function(contentSize){
7586             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7587         },
7588
7589         /**
7590          * Returns the width and height of the viewport.
7591          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7592          */
7593         getViewSize : function(){
7594             var d = this.dom, doc = document, aw = 0, ah = 0;
7595             if(d == doc || d == doc.body){
7596                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7597             }else{
7598                 return {
7599                     width : d.clientWidth,
7600                     height: d.clientHeight
7601                 };
7602             }
7603         },
7604
7605         /**
7606          * Returns the value of the "value" attribute
7607          * @param {Boolean} asNumber true to parse the value as a number
7608          * @return {String/Number}
7609          */
7610         getValue : function(asNumber){
7611             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7612         },
7613
7614         // private
7615         adjustWidth : function(width){
7616             if(typeof width == "number"){
7617                 if(this.autoBoxAdjust && !this.isBorderBox()){
7618                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7619                 }
7620                 if(width < 0){
7621                     width = 0;
7622                 }
7623             }
7624             return width;
7625         },
7626
7627         // private
7628         adjustHeight : function(height){
7629             if(typeof height == "number"){
7630                if(this.autoBoxAdjust && !this.isBorderBox()){
7631                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7632                }
7633                if(height < 0){
7634                    height = 0;
7635                }
7636             }
7637             return height;
7638         },
7639
7640         /**
7641          * Set the width of the element
7642          * @param {Number} width The new width
7643          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7644          * @return {Roo.Element} this
7645          */
7646         setWidth : function(width, animate){
7647             width = this.adjustWidth(width);
7648             if(!animate || !A){
7649                 this.dom.style.width = this.addUnits(width);
7650             }else{
7651                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7652             }
7653             return this;
7654         },
7655
7656         /**
7657          * Set the height of the element
7658          * @param {Number} height The new height
7659          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662          setHeight : function(height, animate){
7663             height = this.adjustHeight(height);
7664             if(!animate || !A){
7665                 this.dom.style.height = this.addUnits(height);
7666             }else{
7667                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7668             }
7669             return this;
7670         },
7671
7672         /**
7673          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7674          * @param {Number} width The new width
7675          * @param {Number} height The new height
7676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7677          * @return {Roo.Element} this
7678          */
7679          setSize : function(width, height, animate){
7680             if(typeof width == "object"){ // in case of object from getSize()
7681                 height = width.height; width = width.width;
7682             }
7683             width = this.adjustWidth(width); height = this.adjustHeight(height);
7684             if(!animate || !A){
7685                 this.dom.style.width = this.addUnits(width);
7686                 this.dom.style.height = this.addUnits(height);
7687             }else{
7688                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7689             }
7690             return this;
7691         },
7692
7693         /**
7694          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7695          * @param {Number} x X value for new position (coordinates are page-based)
7696          * @param {Number} y Y value for new position (coordinates are page-based)
7697          * @param {Number} width The new width
7698          * @param {Number} height The new height
7699          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7700          * @return {Roo.Element} this
7701          */
7702         setBounds : function(x, y, width, height, animate){
7703             if(!animate || !A){
7704                 this.setSize(width, height);
7705                 this.setLocation(x, y);
7706             }else{
7707                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7708                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7709                               this.preanim(arguments, 4), 'motion');
7710             }
7711             return this;
7712         },
7713
7714         /**
7715          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7716          * @param {Roo.lib.Region} region The region to fill
7717          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setRegion : function(region, animate){
7721             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7722             return this;
7723         },
7724
7725         /**
7726          * Appends an event handler
7727          *
7728          * @param {String}   eventName     The type of event to append
7729          * @param {Function} fn        The method the event invokes
7730          * @param {Object} scope       (optional) The scope (this object) of the fn
7731          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7732          */
7733         addListener : function(eventName, fn, scope, options){
7734             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7735         },
7736
7737         /**
7738          * Removes an event handler from this element
7739          * @param {String} eventName the type of event to remove
7740          * @param {Function} fn the method the event invokes
7741          * @return {Roo.Element} this
7742          */
7743         removeListener : function(eventName, fn){
7744             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7745             return this;
7746         },
7747
7748         /**
7749          * Removes all previous added listeners from this element
7750          * @return {Roo.Element} this
7751          */
7752         removeAllListeners : function(){
7753             E.purgeElement(this.dom);
7754             return this;
7755         },
7756
7757         relayEvent : function(eventName, observable){
7758             this.on(eventName, function(e){
7759                 observable.fireEvent(eventName, e);
7760             });
7761         },
7762
7763         /**
7764          * Set the opacity of the element
7765          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7766          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7767          * @return {Roo.Element} this
7768          */
7769          setOpacity : function(opacity, animate){
7770             if(!animate || !A){
7771                 var s = this.dom.style;
7772                 if(Roo.isIE){
7773                     s.zoom = 1;
7774                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7775                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7776                 }else{
7777                     s.opacity = opacity;
7778                 }
7779             }else{
7780                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7781             }
7782             return this;
7783         },
7784
7785         /**
7786          * Gets the left X coordinate
7787          * @param {Boolean} local True to get the local css position instead of page coordinate
7788          * @return {Number}
7789          */
7790         getLeft : function(local){
7791             if(!local){
7792                 return this.getX();
7793             }else{
7794                 return parseInt(this.getStyle("left"), 10) || 0;
7795             }
7796         },
7797
7798         /**
7799          * Gets the right X coordinate of the element (element X position + element width)
7800          * @param {Boolean} local True to get the local css position instead of page coordinate
7801          * @return {Number}
7802          */
7803         getRight : function(local){
7804             if(!local){
7805                 return this.getX() + this.getWidth();
7806             }else{
7807                 return (this.getLeft(true) + this.getWidth()) || 0;
7808             }
7809         },
7810
7811         /**
7812          * Gets the top Y coordinate
7813          * @param {Boolean} local True to get the local css position instead of page coordinate
7814          * @return {Number}
7815          */
7816         getTop : function(local) {
7817             if(!local){
7818                 return this.getY();
7819             }else{
7820                 return parseInt(this.getStyle("top"), 10) || 0;
7821             }
7822         },
7823
7824         /**
7825          * Gets the bottom Y coordinate of the element (element Y position + element height)
7826          * @param {Boolean} local True to get the local css position instead of page coordinate
7827          * @return {Number}
7828          */
7829         getBottom : function(local){
7830             if(!local){
7831                 return this.getY() + this.getHeight();
7832             }else{
7833                 return (this.getTop(true) + this.getHeight()) || 0;
7834             }
7835         },
7836
7837         /**
7838         * Initializes positioning on this element. If a desired position is not passed, it will make the
7839         * the element positioned relative IF it is not already positioned.
7840         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7841         * @param {Number} zIndex (optional) The zIndex to apply
7842         * @param {Number} x (optional) Set the page X position
7843         * @param {Number} y (optional) Set the page Y position
7844         */
7845         position : function(pos, zIndex, x, y){
7846             if(!pos){
7847                if(this.getStyle('position') == 'static'){
7848                    this.setStyle('position', 'relative');
7849                }
7850             }else{
7851                 this.setStyle("position", pos);
7852             }
7853             if(zIndex){
7854                 this.setStyle("z-index", zIndex);
7855             }
7856             if(x !== undefined && y !== undefined){
7857                 this.setXY([x, y]);
7858             }else if(x !== undefined){
7859                 this.setX(x);
7860             }else if(y !== undefined){
7861                 this.setY(y);
7862             }
7863         },
7864
7865         /**
7866         * Clear positioning back to the default when the document was loaded
7867         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7868         * @return {Roo.Element} this
7869          */
7870         clearPositioning : function(value){
7871             value = value ||'';
7872             this.setStyle({
7873                 "left": value,
7874                 "right": value,
7875                 "top": value,
7876                 "bottom": value,
7877                 "z-index": "",
7878                 "position" : "static"
7879             });
7880             return this;
7881         },
7882
7883         /**
7884         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7885         * snapshot before performing an update and then restoring the element.
7886         * @return {Object}
7887         */
7888         getPositioning : function(){
7889             var l = this.getStyle("left");
7890             var t = this.getStyle("top");
7891             return {
7892                 "position" : this.getStyle("position"),
7893                 "left" : l,
7894                 "right" : l ? "" : this.getStyle("right"),
7895                 "top" : t,
7896                 "bottom" : t ? "" : this.getStyle("bottom"),
7897                 "z-index" : this.getStyle("z-index")
7898             };
7899         },
7900
7901         /**
7902          * Gets the width of the border(s) for the specified side(s)
7903          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7904          * passing lr would get the border (l)eft width + the border (r)ight width.
7905          * @return {Number} The width of the sides passed added together
7906          */
7907         getBorderWidth : function(side){
7908             return this.addStyles(side, El.borders);
7909         },
7910
7911         /**
7912          * Gets the width of the padding(s) for the specified side(s)
7913          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7914          * passing lr would get the padding (l)eft + the padding (r)ight.
7915          * @return {Number} The padding of the sides passed added together
7916          */
7917         getPadding : function(side){
7918             return this.addStyles(side, El.paddings);
7919         },
7920
7921         /**
7922         * Set positioning with an object returned by getPositioning().
7923         * @param {Object} posCfg
7924         * @return {Roo.Element} this
7925          */
7926         setPositioning : function(pc){
7927             this.applyStyles(pc);
7928             if(pc.right == "auto"){
7929                 this.dom.style.right = "";
7930             }
7931             if(pc.bottom == "auto"){
7932                 this.dom.style.bottom = "";
7933             }
7934             return this;
7935         },
7936
7937         // private
7938         fixDisplay : function(){
7939             if(this.getStyle("display") == "none"){
7940                 this.setStyle("visibility", "hidden");
7941                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7942                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7943                     this.setStyle("display", "block");
7944                 }
7945             }
7946         },
7947
7948         /**
7949          * Quick set left and top adding default units
7950          * @param {String} left The left CSS property value
7951          * @param {String} top The top CSS property value
7952          * @return {Roo.Element} this
7953          */
7954          setLeftTop : function(left, top){
7955             this.dom.style.left = this.addUnits(left);
7956             this.dom.style.top = this.addUnits(top);
7957             return this;
7958         },
7959
7960         /**
7961          * Move this element relative to its current position.
7962          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7963          * @param {Number} distance How far to move the element in pixels
7964          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7965          * @return {Roo.Element} this
7966          */
7967          move : function(direction, distance, animate){
7968             var xy = this.getXY();
7969             direction = direction.toLowerCase();
7970             switch(direction){
7971                 case "l":
7972                 case "left":
7973                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7974                     break;
7975                case "r":
7976                case "right":
7977                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
7978                     break;
7979                case "t":
7980                case "top":
7981                case "up":
7982                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
7983                     break;
7984                case "b":
7985                case "bottom":
7986                case "down":
7987                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
7988                     break;
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
7995          * @return {Roo.Element} this
7996          */
7997         clip : function(){
7998             if(!this.isClipped){
7999                this.isClipped = true;
8000                this.originalClip = {
8001                    "o": this.getStyle("overflow"),
8002                    "x": this.getStyle("overflow-x"),
8003                    "y": this.getStyle("overflow-y")
8004                };
8005                this.setStyle("overflow", "hidden");
8006                this.setStyle("overflow-x", "hidden");
8007                this.setStyle("overflow-y", "hidden");
8008             }
8009             return this;
8010         },
8011
8012         /**
8013          *  Return clipping (overflow) to original clipping before clip() was called
8014          * @return {Roo.Element} this
8015          */
8016         unclip : function(){
8017             if(this.isClipped){
8018                 this.isClipped = false;
8019                 var o = this.originalClip;
8020                 if(o.o){this.setStyle("overflow", o.o);}
8021                 if(o.x){this.setStyle("overflow-x", o.x);}
8022                 if(o.y){this.setStyle("overflow-y", o.y);}
8023             }
8024             return this;
8025         },
8026
8027
8028         /**
8029          * Gets the x,y coordinates specified by the anchor position on the element.
8030          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8031          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8032          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8033          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8034          * @return {Array} [x, y] An array containing the element's x and y coordinates
8035          */
8036         getAnchorXY : function(anchor, local, s){
8037             //Passing a different size is useful for pre-calculating anchors,
8038             //especially for anchored animations that change the el size.
8039
8040             var w, h, vp = false;
8041             if(!s){
8042                 var d = this.dom;
8043                 if(d == document.body || d == document){
8044                     vp = true;
8045                     w = D.getViewWidth(); h = D.getViewHeight();
8046                 }else{
8047                     w = this.getWidth(); h = this.getHeight();
8048                 }
8049             }else{
8050                 w = s.width;  h = s.height;
8051             }
8052             var x = 0, y = 0, r = Math.round;
8053             switch((anchor || "tl").toLowerCase()){
8054                 case "c":
8055                     x = r(w*.5);
8056                     y = r(h*.5);
8057                 break;
8058                 case "t":
8059                     x = r(w*.5);
8060                     y = 0;
8061                 break;
8062                 case "l":
8063                     x = 0;
8064                     y = r(h*.5);
8065                 break;
8066                 case "r":
8067                     x = w;
8068                     y = r(h*.5);
8069                 break;
8070                 case "b":
8071                     x = r(w*.5);
8072                     y = h;
8073                 break;
8074                 case "tl":
8075                     x = 0;
8076                     y = 0;
8077                 break;
8078                 case "bl":
8079                     x = 0;
8080                     y = h;
8081                 break;
8082                 case "br":
8083                     x = w;
8084                     y = h;
8085                 break;
8086                 case "tr":
8087                     x = w;
8088                     y = 0;
8089                 break;
8090             }
8091             if(local === true){
8092                 return [x, y];
8093             }
8094             if(vp){
8095                 var sc = this.getScroll();
8096                 return [x + sc.left, y + sc.top];
8097             }
8098             //Add the element's offset xy
8099             var o = this.getXY();
8100             return [x+o[0], y+o[1]];
8101         },
8102
8103         /**
8104          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8105          * supported position values.
8106          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8107          * @param {String} position The position to align to.
8108          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8109          * @return {Array} [x, y]
8110          */
8111         getAlignToXY : function(el, p, o){
8112             el = Roo.get(el);
8113             var d = this.dom;
8114             if(!el.dom){
8115                 throw "Element.alignTo with an element that doesn't exist";
8116             }
8117             var c = false; //constrain to viewport
8118             var p1 = "", p2 = "";
8119             o = o || [0,0];
8120
8121             if(!p){
8122                 p = "tl-bl";
8123             }else if(p == "?"){
8124                 p = "tl-bl?";
8125             }else if(p.indexOf("-") == -1){
8126                 p = "tl-" + p;
8127             }
8128             p = p.toLowerCase();
8129             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8130             if(!m){
8131                throw "Element.alignTo with an invalid alignment " + p;
8132             }
8133             p1 = m[1]; p2 = m[2]; c = !!m[3];
8134
8135             //Subtract the aligned el's internal xy from the target's offset xy
8136             //plus custom offset to get the aligned el's new offset xy
8137             var a1 = this.getAnchorXY(p1, true);
8138             var a2 = el.getAnchorXY(p2, false);
8139             var x = a2[0] - a1[0] + o[0];
8140             var y = a2[1] - a1[1] + o[1];
8141             if(c){
8142                 //constrain the aligned el to viewport if necessary
8143                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8144                 // 5px of margin for ie
8145                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8146
8147                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8148                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8149                 //otherwise swap the aligned el to the opposite border of the target.
8150                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8151                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8152                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8153                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8154
8155                var doc = document;
8156                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8157                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8158
8159                if((x+w) > dw + scrollX){
8160                     x = swapX ? r.left-w : dw+scrollX-w;
8161                 }
8162                if(x < scrollX){
8163                    x = swapX ? r.right : scrollX;
8164                }
8165                if((y+h) > dh + scrollY){
8166                     y = swapY ? r.top-h : dh+scrollY-h;
8167                 }
8168                if (y < scrollY){
8169                    y = swapY ? r.bottom : scrollY;
8170                }
8171             }
8172             return [x,y];
8173         },
8174
8175         // private
8176         getConstrainToXY : function(){
8177             var os = {top:0, left:0, bottom:0, right: 0};
8178
8179             return function(el, local, offsets, proposedXY){
8180                 el = Roo.get(el);
8181                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8182
8183                 var vw, vh, vx = 0, vy = 0;
8184                 if(el.dom == document.body || el.dom == document){
8185                     vw = Roo.lib.Dom.getViewWidth();
8186                     vh = Roo.lib.Dom.getViewHeight();
8187                 }else{
8188                     vw = el.dom.clientWidth;
8189                     vh = el.dom.clientHeight;
8190                     if(!local){
8191                         var vxy = el.getXY();
8192                         vx = vxy[0];
8193                         vy = vxy[1];
8194                     }
8195                 }
8196
8197                 var s = el.getScroll();
8198
8199                 vx += offsets.left + s.left;
8200                 vy += offsets.top + s.top;
8201
8202                 vw -= offsets.right;
8203                 vh -= offsets.bottom;
8204
8205                 var vr = vx+vw;
8206                 var vb = vy+vh;
8207
8208                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8209                 var x = xy[0], y = xy[1];
8210                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8211
8212                 // only move it if it needs it
8213                 var moved = false;
8214
8215                 // first validate right/bottom
8216                 if((x + w) > vr){
8217                     x = vr - w;
8218                     moved = true;
8219                 }
8220                 if((y + h) > vb){
8221                     y = vb - h;
8222                     moved = true;
8223                 }
8224                 // then make sure top/left isn't negative
8225                 if(x < vx){
8226                     x = vx;
8227                     moved = true;
8228                 }
8229                 if(y < vy){
8230                     y = vy;
8231                     moved = true;
8232                 }
8233                 return moved ? [x, y] : false;
8234             };
8235         }(),
8236
8237         // private
8238         adjustForConstraints : function(xy, parent, offsets){
8239             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8240         },
8241
8242         /**
8243          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8244          * document it aligns it to the viewport.
8245          * The position parameter is optional, and can be specified in any one of the following formats:
8246          * <ul>
8247          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8248          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8249          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8250          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8251          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8252          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8253          * </ul>
8254          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8255          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8256          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8257          * that specified in order to enforce the viewport constraints.
8258          * Following are all of the supported anchor positions:
8259     <pre>
8260     Value  Description
8261     -----  -----------------------------
8262     tl     The top left corner (default)
8263     t      The center of the top edge
8264     tr     The top right corner
8265     l      The center of the left edge
8266     c      In the center of the element
8267     r      The center of the right edge
8268     bl     The bottom left corner
8269     b      The center of the bottom edge
8270     br     The bottom right corner
8271     </pre>
8272     Example Usage:
8273     <pre><code>
8274     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8275     el.alignTo("other-el");
8276
8277     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8278     el.alignTo("other-el", "tr?");
8279
8280     // align the bottom right corner of el with the center left edge of other-el
8281     el.alignTo("other-el", "br-l?");
8282
8283     // align the center of el with the bottom left corner of other-el and
8284     // adjust the x position by -6 pixels (and the y position by 0)
8285     el.alignTo("other-el", "c-bl", [-6, 0]);
8286     </code></pre>
8287          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8288          * @param {String} position The position to align to.
8289          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8290          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8291          * @return {Roo.Element} this
8292          */
8293         alignTo : function(element, position, offsets, animate){
8294             var xy = this.getAlignToXY(element, position, offsets);
8295             this.setXY(xy, this.preanim(arguments, 3));
8296             return this;
8297         },
8298
8299         /**
8300          * Anchors an element to another element and realigns it when the window is resized.
8301          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8302          * @param {String} position The position to align to.
8303          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8304          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8305          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8306          * is a number, it is used as the buffer delay (defaults to 50ms).
8307          * @param {Function} callback The function to call after the animation finishes
8308          * @return {Roo.Element} this
8309          */
8310         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8311             var action = function(){
8312                 this.alignTo(el, alignment, offsets, animate);
8313                 Roo.callback(callback, this);
8314             };
8315             Roo.EventManager.onWindowResize(action, this);
8316             var tm = typeof monitorScroll;
8317             if(tm != 'undefined'){
8318                 Roo.EventManager.on(window, 'scroll', action, this,
8319                     {buffer: tm == 'number' ? monitorScroll : 50});
8320             }
8321             action.call(this); // align immediately
8322             return this;
8323         },
8324         /**
8325          * Clears any opacity settings from this element. Required in some cases for IE.
8326          * @return {Roo.Element} this
8327          */
8328         clearOpacity : function(){
8329             if (window.ActiveXObject) {
8330                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8331                     this.dom.style.filter = "";
8332                 }
8333             } else {
8334                 this.dom.style.opacity = "";
8335                 this.dom.style["-moz-opacity"] = "";
8336                 this.dom.style["-khtml-opacity"] = "";
8337             }
8338             return this;
8339         },
8340
8341         /**
8342          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8343          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8344          * @return {Roo.Element} this
8345          */
8346         hide : function(animate){
8347             this.setVisible(false, this.preanim(arguments, 0));
8348             return this;
8349         },
8350
8351         /**
8352         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8353         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8354          * @return {Roo.Element} this
8355          */
8356         show : function(animate){
8357             this.setVisible(true, this.preanim(arguments, 0));
8358             return this;
8359         },
8360
8361         /**
8362          * @private Test if size has a unit, otherwise appends the default
8363          */
8364         addUnits : function(size){
8365             return Roo.Element.addUnits(size, this.defaultUnit);
8366         },
8367
8368         /**
8369          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8370          * @return {Roo.Element} this
8371          */
8372         beginMeasure : function(){
8373             var el = this.dom;
8374             if(el.offsetWidth || el.offsetHeight){
8375                 return this; // offsets work already
8376             }
8377             var changed = [];
8378             var p = this.dom, b = document.body; // start with this element
8379             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8380                 var pe = Roo.get(p);
8381                 if(pe.getStyle('display') == 'none'){
8382                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8383                     p.style.visibility = "hidden";
8384                     p.style.display = "block";
8385                 }
8386                 p = p.parentNode;
8387             }
8388             this._measureChanged = changed;
8389             return this;
8390
8391         },
8392
8393         /**
8394          * Restores displays to before beginMeasure was called
8395          * @return {Roo.Element} this
8396          */
8397         endMeasure : function(){
8398             var changed = this._measureChanged;
8399             if(changed){
8400                 for(var i = 0, len = changed.length; i < len; i++) {
8401                     var r = changed[i];
8402                     r.el.style.visibility = r.visibility;
8403                     r.el.style.display = "none";
8404                 }
8405                 this._measureChanged = null;
8406             }
8407             return this;
8408         },
8409
8410         /**
8411         * Update the innerHTML of this element, optionally searching for and processing scripts
8412         * @param {String} html The new HTML
8413         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8414         * @param {Function} callback For async script loading you can be noticed when the update completes
8415         * @return {Roo.Element} this
8416          */
8417         update : function(html, loadScripts, callback){
8418             if(typeof html == "undefined"){
8419                 html = "";
8420             }
8421             if(loadScripts !== true){
8422                 this.dom.innerHTML = html;
8423                 if(typeof callback == "function"){
8424                     callback();
8425                 }
8426                 return this;
8427             }
8428             var id = Roo.id();
8429             var dom = this.dom;
8430
8431             html += '<span id="' + id + '"></span>';
8432
8433             E.onAvailable(id, function(){
8434                 var hd = document.getElementsByTagName("head")[0];
8435                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8436                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8437                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8438
8439                 var match;
8440                 while(match = re.exec(html)){
8441                     var attrs = match[1];
8442                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8443                     if(srcMatch && srcMatch[2]){
8444                        var s = document.createElement("script");
8445                        s.src = srcMatch[2];
8446                        var typeMatch = attrs.match(typeRe);
8447                        if(typeMatch && typeMatch[2]){
8448                            s.type = typeMatch[2];
8449                        }
8450                        hd.appendChild(s);
8451                     }else if(match[2] && match[2].length > 0){
8452                         if(window.execScript) {
8453                            window.execScript(match[2]);
8454                         } else {
8455                             /**
8456                              * eval:var:id
8457                              * eval:var:dom
8458                              * eval:var:html
8459                              * 
8460                              */
8461                            window.eval(match[2]);
8462                         }
8463                     }
8464                 }
8465                 var el = document.getElementById(id);
8466                 if(el){el.parentNode.removeChild(el);}
8467                 if(typeof callback == "function"){
8468                     callback();
8469                 }
8470             });
8471             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8472             return this;
8473         },
8474
8475         /**
8476          * Direct access to the UpdateManager update() method (takes the same parameters).
8477          * @param {String/Function} url The url for this request or a function to call to get the url
8478          * @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}
8479          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8480          * @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.
8481          * @return {Roo.Element} this
8482          */
8483         load : function(){
8484             var um = this.getUpdateManager();
8485             um.update.apply(um, arguments);
8486             return this;
8487         },
8488
8489         /**
8490         * Gets this element's UpdateManager
8491         * @return {Roo.UpdateManager} The UpdateManager
8492         */
8493         getUpdateManager : function(){
8494             if(!this.updateManager){
8495                 this.updateManager = new Roo.UpdateManager(this);
8496             }
8497             return this.updateManager;
8498         },
8499
8500         /**
8501          * Disables text selection for this element (normalized across browsers)
8502          * @return {Roo.Element} this
8503          */
8504         unselectable : function(){
8505             this.dom.unselectable = "on";
8506             this.swallowEvent("selectstart", true);
8507             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8508             this.addClass("x-unselectable");
8509             return this;
8510         },
8511
8512         /**
8513         * Calculates the x, y to center this element on the screen
8514         * @return {Array} The x, y values [x, y]
8515         */
8516         getCenterXY : function(){
8517             return this.getAlignToXY(document, 'c-c');
8518         },
8519
8520         /**
8521         * Centers the Element in either the viewport, or another Element.
8522         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8523         */
8524         center : function(centerIn){
8525             this.alignTo(centerIn || document, 'c-c');
8526             return this;
8527         },
8528
8529         /**
8530          * Tests various css rules/browsers to determine if this element uses a border box
8531          * @return {Boolean}
8532          */
8533         isBorderBox : function(){
8534             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8535         },
8536
8537         /**
8538          * Return a box {x, y, width, height} that can be used to set another elements
8539          * size/location to match this element.
8540          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8541          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8542          * @return {Object} box An object in the format {x, y, width, height}
8543          */
8544         getBox : function(contentBox, local){
8545             var xy;
8546             if(!local){
8547                 xy = this.getXY();
8548             }else{
8549                 var left = parseInt(this.getStyle("left"), 10) || 0;
8550                 var top = parseInt(this.getStyle("top"), 10) || 0;
8551                 xy = [left, top];
8552             }
8553             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8554             if(!contentBox){
8555                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8556             }else{
8557                 var l = this.getBorderWidth("l")+this.getPadding("l");
8558                 var r = this.getBorderWidth("r")+this.getPadding("r");
8559                 var t = this.getBorderWidth("t")+this.getPadding("t");
8560                 var b = this.getBorderWidth("b")+this.getPadding("b");
8561                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8562             }
8563             bx.right = bx.x + bx.width;
8564             bx.bottom = bx.y + bx.height;
8565             return bx;
8566         },
8567
8568         /**
8569          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8570          for more information about the sides.
8571          * @param {String} sides
8572          * @return {Number}
8573          */
8574         getFrameWidth : function(sides, onlyContentBox){
8575             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8576         },
8577
8578         /**
8579          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8580          * @param {Object} box The box to fill {x, y, width, height}
8581          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8582          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8583          * @return {Roo.Element} this
8584          */
8585         setBox : function(box, adjust, animate){
8586             var w = box.width, h = box.height;
8587             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8588                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8589                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8590             }
8591             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8592             return this;
8593         },
8594
8595         /**
8596          * Forces the browser to repaint this element
8597          * @return {Roo.Element} this
8598          */
8599          repaint : function(){
8600             var dom = this.dom;
8601             this.addClass("x-repaint");
8602             setTimeout(function(){
8603                 Roo.get(dom).removeClass("x-repaint");
8604             }, 1);
8605             return this;
8606         },
8607
8608         /**
8609          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8610          * then it returns the calculated width of the sides (see getPadding)
8611          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8612          * @return {Object/Number}
8613          */
8614         getMargins : function(side){
8615             if(!side){
8616                 return {
8617                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8618                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8619                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8620                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8621                 };
8622             }else{
8623                 return this.addStyles(side, El.margins);
8624              }
8625         },
8626
8627         // private
8628         addStyles : function(sides, styles){
8629             var val = 0, v, w;
8630             for(var i = 0, len = sides.length; i < len; i++){
8631                 v = this.getStyle(styles[sides.charAt(i)]);
8632                 if(v){
8633                      w = parseInt(v, 10);
8634                      if(w){ val += w; }
8635                 }
8636             }
8637             return val;
8638         },
8639
8640         /**
8641          * Creates a proxy element of this element
8642          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8643          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8644          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8645          * @return {Roo.Element} The new proxy element
8646          */
8647         createProxy : function(config, renderTo, matchBox){
8648             if(renderTo){
8649                 renderTo = Roo.getDom(renderTo);
8650             }else{
8651                 renderTo = document.body;
8652             }
8653             config = typeof config == "object" ?
8654                 config : {tag : "div", cls: config};
8655             var proxy = Roo.DomHelper.append(renderTo, config, true);
8656             if(matchBox){
8657                proxy.setBox(this.getBox());
8658             }
8659             return proxy;
8660         },
8661
8662         /**
8663          * Puts a mask over this element to disable user interaction. Requires core.css.
8664          * This method can only be applied to elements which accept child nodes.
8665          * @param {String} msg (optional) A message to display in the mask
8666          * @param {String} msgCls (optional) A css class to apply to the msg element
8667          * @return {Element} The mask  element
8668          */
8669         mask : function(msg, msgCls){
8670             if(this.getStyle("position") == "static"){
8671                 this.setStyle("position", "relative");
8672             }
8673             if(!this._mask){
8674                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8675             }
8676             this.addClass("x-masked");
8677             this._mask.setDisplayed(true);
8678             if(typeof msg == 'string'){
8679                 if(!this._maskMsg){
8680                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8681                 }
8682                 var mm = this._maskMsg;
8683                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8684                 mm.dom.firstChild.innerHTML = msg;
8685                 mm.setDisplayed(true);
8686                 mm.center(this);
8687             }
8688             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8689                 this._mask.setHeight(this.getHeight());
8690             }
8691             return this._mask;
8692         },
8693
8694         /**
8695          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8696          * it is cached for reuse.
8697          */
8698         unmask : function(removeEl){
8699             if(this._mask){
8700                 if(removeEl === true){
8701                     this._mask.remove();
8702                     delete this._mask;
8703                     if(this._maskMsg){
8704                         this._maskMsg.remove();
8705                         delete this._maskMsg;
8706                     }
8707                 }else{
8708                     this._mask.setDisplayed(false);
8709                     if(this._maskMsg){
8710                         this._maskMsg.setDisplayed(false);
8711                     }
8712                 }
8713             }
8714             this.removeClass("x-masked");
8715         },
8716
8717         /**
8718          * Returns true if this element is masked
8719          * @return {Boolean}
8720          */
8721         isMasked : function(){
8722             return this._mask && this._mask.isVisible();
8723         },
8724
8725         /**
8726          * Creates an iframe shim for this element to keep selects and other windowed objects from
8727          * showing through.
8728          * @return {Roo.Element} The new shim element
8729          */
8730         createShim : function(){
8731             var el = document.createElement('iframe');
8732             el.frameBorder = 'no';
8733             el.className = 'roo-shim';
8734             if(Roo.isIE && Roo.isSecure){
8735                 el.src = Roo.SSL_SECURE_URL;
8736             }
8737             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8738             shim.autoBoxAdjust = false;
8739             return shim;
8740         },
8741
8742         /**
8743          * Removes this element from the DOM and deletes it from the cache
8744          */
8745         remove : function(){
8746             if(this.dom.parentNode){
8747                 this.dom.parentNode.removeChild(this.dom);
8748             }
8749             delete El.cache[this.dom.id];
8750         },
8751
8752         /**
8753          * Sets up event handlers to add and remove a css class when the mouse is over this element
8754          * @param {String} className
8755          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8756          * mouseout events for children elements
8757          * @return {Roo.Element} this
8758          */
8759         addClassOnOver : function(className, preventFlicker){
8760             this.on("mouseover", function(){
8761                 Roo.fly(this, '_internal').addClass(className);
8762             }, this.dom);
8763             var removeFn = function(e){
8764                 if(preventFlicker !== true || !e.within(this, true)){
8765                     Roo.fly(this, '_internal').removeClass(className);
8766                 }
8767             };
8768             this.on("mouseout", removeFn, this.dom);
8769             return this;
8770         },
8771
8772         /**
8773          * Sets up event handlers to add and remove a css class when this element has the focus
8774          * @param {String} className
8775          * @return {Roo.Element} this
8776          */
8777         addClassOnFocus : function(className){
8778             this.on("focus", function(){
8779                 Roo.fly(this, '_internal').addClass(className);
8780             }, this.dom);
8781             this.on("blur", function(){
8782                 Roo.fly(this, '_internal').removeClass(className);
8783             }, this.dom);
8784             return this;
8785         },
8786         /**
8787          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8788          * @param {String} className
8789          * @return {Roo.Element} this
8790          */
8791         addClassOnClick : function(className){
8792             var dom = this.dom;
8793             this.on("mousedown", function(){
8794                 Roo.fly(dom, '_internal').addClass(className);
8795                 var d = Roo.get(document);
8796                 var fn = function(){
8797                     Roo.fly(dom, '_internal').removeClass(className);
8798                     d.removeListener("mouseup", fn);
8799                 };
8800                 d.on("mouseup", fn);
8801             });
8802             return this;
8803         },
8804
8805         /**
8806          * Stops the specified event from bubbling and optionally prevents the default action
8807          * @param {String} eventName
8808          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8809          * @return {Roo.Element} this
8810          */
8811         swallowEvent : function(eventName, preventDefault){
8812             var fn = function(e){
8813                 e.stopPropagation();
8814                 if(preventDefault){
8815                     e.preventDefault();
8816                 }
8817             };
8818             if(eventName instanceof Array){
8819                 for(var i = 0, len = eventName.length; i < len; i++){
8820                      this.on(eventName[i], fn);
8821                 }
8822                 return this;
8823             }
8824             this.on(eventName, fn);
8825             return this;
8826         },
8827
8828         /**
8829          * @private
8830          */
8831       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8832
8833         /**
8834          * Sizes this element to its parent element's dimensions performing
8835          * neccessary box adjustments.
8836          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8837          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8838          * @return {Roo.Element} this
8839          */
8840         fitToParent : function(monitorResize, targetParent) {
8841           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8842           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8843           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8844             return;
8845           }
8846           var p = Roo.get(targetParent || this.dom.parentNode);
8847           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8848           if (monitorResize === true) {
8849             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8850             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8851           }
8852           return this;
8853         },
8854
8855         /**
8856          * Gets the next sibling, skipping text nodes
8857          * @return {HTMLElement} The next sibling or null
8858          */
8859         getNextSibling : function(){
8860             var n = this.dom.nextSibling;
8861             while(n && n.nodeType != 1){
8862                 n = n.nextSibling;
8863             }
8864             return n;
8865         },
8866
8867         /**
8868          * Gets the previous sibling, skipping text nodes
8869          * @return {HTMLElement} The previous sibling or null
8870          */
8871         getPrevSibling : function(){
8872             var n = this.dom.previousSibling;
8873             while(n && n.nodeType != 1){
8874                 n = n.previousSibling;
8875             }
8876             return n;
8877         },
8878
8879
8880         /**
8881          * Appends the passed element(s) to this element
8882          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8883          * @return {Roo.Element} this
8884          */
8885         appendChild: function(el){
8886             el = Roo.get(el);
8887             el.appendTo(this);
8888             return this;
8889         },
8890
8891         /**
8892          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8893          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8894          * automatically generated with the specified attributes.
8895          * @param {HTMLElement} insertBefore (optional) a child element of this element
8896          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8897          * @return {Roo.Element} The new child element
8898          */
8899         createChild: function(config, insertBefore, returnDom){
8900             config = config || {tag:'div'};
8901             if(insertBefore){
8902                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8903             }
8904             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8905         },
8906
8907         /**
8908          * Appends this element to the passed element
8909          * @param {String/HTMLElement/Element} el The new parent element
8910          * @return {Roo.Element} this
8911          */
8912         appendTo: function(el){
8913             el = Roo.getDom(el);
8914             el.appendChild(this.dom);
8915             return this;
8916         },
8917
8918         /**
8919          * Inserts this element before the passed element in the DOM
8920          * @param {String/HTMLElement/Element} el The element to insert before
8921          * @return {Roo.Element} this
8922          */
8923         insertBefore: function(el){
8924             el = Roo.getDom(el);
8925             el.parentNode.insertBefore(this.dom, el);
8926             return this;
8927         },
8928
8929         /**
8930          * Inserts this element after the passed element in the DOM
8931          * @param {String/HTMLElement/Element} el The element to insert after
8932          * @return {Roo.Element} this
8933          */
8934         insertAfter: function(el){
8935             el = Roo.getDom(el);
8936             el.parentNode.insertBefore(this.dom, el.nextSibling);
8937             return this;
8938         },
8939
8940         /**
8941          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8942          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8943          * @return {Roo.Element} The new child
8944          */
8945         insertFirst: function(el, returnDom){
8946             el = el || {};
8947             if(typeof el == 'object' && !el.nodeType){ // dh config
8948                 return this.createChild(el, this.dom.firstChild, returnDom);
8949             }else{
8950                 el = Roo.getDom(el);
8951                 this.dom.insertBefore(el, this.dom.firstChild);
8952                 return !returnDom ? Roo.get(el) : el;
8953             }
8954         },
8955
8956         /**
8957          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8958          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8959          * @param {String} where (optional) 'before' or 'after' defaults to before
8960          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8961          * @return {Roo.Element} the inserted Element
8962          */
8963         insertSibling: function(el, where, returnDom){
8964             where = where ? where.toLowerCase() : 'before';
8965             el = el || {};
8966             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8967
8968             if(typeof el == 'object' && !el.nodeType){ // dh config
8969                 if(where == 'after' && !this.dom.nextSibling){
8970                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8971                 }else{
8972                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8973                 }
8974
8975             }else{
8976                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
8977                             where == 'before' ? this.dom : this.dom.nextSibling);
8978                 if(!returnDom){
8979                     rt = Roo.get(rt);
8980                 }
8981             }
8982             return rt;
8983         },
8984
8985         /**
8986          * Creates and wraps this element with another element
8987          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
8988          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8989          * @return {HTMLElement/Element} The newly created wrapper element
8990          */
8991         wrap: function(config, returnDom){
8992             if(!config){
8993                 config = {tag: "div"};
8994             }
8995             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
8996             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
8997             return newEl;
8998         },
8999
9000         /**
9001          * Replaces the passed element with this element
9002          * @param {String/HTMLElement/Element} el The element to replace
9003          * @return {Roo.Element} this
9004          */
9005         replace: function(el){
9006             el = Roo.get(el);
9007             this.insertBefore(el);
9008             el.remove();
9009             return this;
9010         },
9011
9012         /**
9013          * Inserts an html fragment into this element
9014          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9015          * @param {String} html The HTML fragment
9016          * @param {Boolean} returnEl True to return an Roo.Element
9017          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9018          */
9019         insertHtml : function(where, html, returnEl){
9020             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9021             return returnEl ? Roo.get(el) : el;
9022         },
9023
9024         /**
9025          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9026          * @param {Object} o The object with the attributes
9027          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9028          * @return {Roo.Element} this
9029          */
9030         set : function(o, useSet){
9031             var el = this.dom;
9032             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9033             for(var attr in o){
9034                 if(attr == "style" || typeof o[attr] == "function") continue;
9035                 if(attr=="cls"){
9036                     el.className = o["cls"];
9037                 }else{
9038                     if(useSet) el.setAttribute(attr, o[attr]);
9039                     else el[attr] = o[attr];
9040                 }
9041             }
9042             if(o.style){
9043                 Roo.DomHelper.applyStyles(el, o.style);
9044             }
9045             return this;
9046         },
9047
9048         /**
9049          * Convenience method for constructing a KeyMap
9050          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9051          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9052          * @param {Function} fn The function to call
9053          * @param {Object} scope (optional) The scope of the function
9054          * @return {Roo.KeyMap} The KeyMap created
9055          */
9056         addKeyListener : function(key, fn, scope){
9057             var config;
9058             if(typeof key != "object" || key instanceof Array){
9059                 config = {
9060                     key: key,
9061                     fn: fn,
9062                     scope: scope
9063                 };
9064             }else{
9065                 config = {
9066                     key : key.key,
9067                     shift : key.shift,
9068                     ctrl : key.ctrl,
9069                     alt : key.alt,
9070                     fn: fn,
9071                     scope: scope
9072                 };
9073             }
9074             return new Roo.KeyMap(this, config);
9075         },
9076
9077         /**
9078          * Creates a KeyMap for this element
9079          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9080          * @return {Roo.KeyMap} The KeyMap created
9081          */
9082         addKeyMap : function(config){
9083             return new Roo.KeyMap(this, config);
9084         },
9085
9086         /**
9087          * Returns true if this element is scrollable.
9088          * @return {Boolean}
9089          */
9090          isScrollable : function(){
9091             var dom = this.dom;
9092             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9093         },
9094
9095         /**
9096          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9097          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9098          * @param {Number} value The new scroll value
9099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9100          * @return {Element} this
9101          */
9102
9103         scrollTo : function(side, value, animate){
9104             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9105             if(!animate || !A){
9106                 this.dom[prop] = value;
9107             }else{
9108                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9109                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9110             }
9111             return this;
9112         },
9113
9114         /**
9115          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9116          * within this element's scrollable range.
9117          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9118          * @param {Number} distance How far to scroll the element in pixels
9119          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9120          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9121          * was scrolled as far as it could go.
9122          */
9123          scroll : function(direction, distance, animate){
9124              if(!this.isScrollable()){
9125                  return;
9126              }
9127              var el = this.dom;
9128              var l = el.scrollLeft, t = el.scrollTop;
9129              var w = el.scrollWidth, h = el.scrollHeight;
9130              var cw = el.clientWidth, ch = el.clientHeight;
9131              direction = direction.toLowerCase();
9132              var scrolled = false;
9133              var a = this.preanim(arguments, 2);
9134              switch(direction){
9135                  case "l":
9136                  case "left":
9137                      if(w - l > cw){
9138                          var v = Math.min(l + distance, w-cw);
9139                          this.scrollTo("left", v, a);
9140                          scrolled = true;
9141                      }
9142                      break;
9143                 case "r":
9144                 case "right":
9145                      if(l > 0){
9146                          var v = Math.max(l - distance, 0);
9147                          this.scrollTo("left", v, a);
9148                          scrolled = true;
9149                      }
9150                      break;
9151                 case "t":
9152                 case "top":
9153                 case "up":
9154                      if(t > 0){
9155                          var v = Math.max(t - distance, 0);
9156                          this.scrollTo("top", v, a);
9157                          scrolled = true;
9158                      }
9159                      break;
9160                 case "b":
9161                 case "bottom":
9162                 case "down":
9163                      if(h - t > ch){
9164                          var v = Math.min(t + distance, h-ch);
9165                          this.scrollTo("top", v, a);
9166                          scrolled = true;
9167                      }
9168                      break;
9169              }
9170              return scrolled;
9171         },
9172
9173         /**
9174          * Translates the passed page coordinates into left/top css values for this element
9175          * @param {Number/Array} x The page x or an array containing [x, y]
9176          * @param {Number} y The page y
9177          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9178          */
9179         translatePoints : function(x, y){
9180             if(typeof x == 'object' || x instanceof Array){
9181                 y = x[1]; x = x[0];
9182             }
9183             var p = this.getStyle('position');
9184             var o = this.getXY();
9185
9186             var l = parseInt(this.getStyle('left'), 10);
9187             var t = parseInt(this.getStyle('top'), 10);
9188
9189             if(isNaN(l)){
9190                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9191             }
9192             if(isNaN(t)){
9193                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9194             }
9195
9196             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9197         },
9198
9199         /**
9200          * Returns the current scroll position of the element.
9201          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9202          */
9203         getScroll : function(){
9204             var d = this.dom, doc = document;
9205             if(d == doc || d == doc.body){
9206                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9207                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9208                 return {left: l, top: t};
9209             }else{
9210                 return {left: d.scrollLeft, top: d.scrollTop};
9211             }
9212         },
9213
9214         /**
9215          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9216          * are convert to standard 6 digit hex color.
9217          * @param {String} attr The css attribute
9218          * @param {String} defaultValue The default value to use when a valid color isn't found
9219          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9220          * YUI color anims.
9221          */
9222         getColor : function(attr, defaultValue, prefix){
9223             var v = this.getStyle(attr);
9224             if(!v || v == "transparent" || v == "inherit") {
9225                 return defaultValue;
9226             }
9227             var color = typeof prefix == "undefined" ? "#" : prefix;
9228             if(v.substr(0, 4) == "rgb("){
9229                 var rvs = v.slice(4, v.length -1).split(",");
9230                 for(var i = 0; i < 3; i++){
9231                     var h = parseInt(rvs[i]).toString(16);
9232                     if(h < 16){
9233                         h = "0" + h;
9234                     }
9235                     color += h;
9236                 }
9237             } else {
9238                 if(v.substr(0, 1) == "#"){
9239                     if(v.length == 4) {
9240                         for(var i = 1; i < 4; i++){
9241                             var c = v.charAt(i);
9242                             color +=  c + c;
9243                         }
9244                     }else if(v.length == 7){
9245                         color += v.substr(1);
9246                     }
9247                 }
9248             }
9249             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9250         },
9251
9252         /**
9253          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9254          * gradient background, rounded corners and a 4-way shadow.
9255          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9256          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9257          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9258          * @return {Roo.Element} this
9259          */
9260         boxWrap : function(cls){
9261             cls = cls || 'x-box';
9262             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9263             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9264             return el;
9265         },
9266
9267         /**
9268          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9269          * @param {String} namespace The namespace in which to look for the attribute
9270          * @param {String} name The attribute name
9271          * @return {String} The attribute value
9272          */
9273         getAttributeNS : Roo.isIE ? function(ns, name){
9274             var d = this.dom;
9275             var type = typeof d[ns+":"+name];
9276             if(type != 'undefined' && type != 'unknown'){
9277                 return d[ns+":"+name];
9278             }
9279             return d[name];
9280         } : function(ns, name){
9281             var d = this.dom;
9282             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9283         }
9284     };
9285
9286     var ep = El.prototype;
9287
9288     /**
9289      * Appends an event handler (Shorthand for addListener)
9290      * @param {String}   eventName     The type of event to append
9291      * @param {Function} fn        The method the event invokes
9292      * @param {Object} scope       (optional) The scope (this object) of the fn
9293      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9294      * @method
9295      */
9296     ep.on = ep.addListener;
9297         // backwards compat
9298     ep.mon = ep.addListener;
9299
9300     /**
9301      * Removes an event handler from this element (shorthand for removeListener)
9302      * @param {String} eventName the type of event to remove
9303      * @param {Function} fn the method the event invokes
9304      * @return {Roo.Element} this
9305      * @method
9306      */
9307     ep.un = ep.removeListener;
9308
9309     /**
9310      * true to automatically adjust width and height settings for box-model issues (default to true)
9311      */
9312     ep.autoBoxAdjust = true;
9313
9314     // private
9315     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9316
9317     // private
9318     El.addUnits = function(v, defaultUnit){
9319         if(v === "" || v == "auto"){
9320             return v;
9321         }
9322         if(v === undefined){
9323             return '';
9324         }
9325         if(typeof v == "number" || !El.unitPattern.test(v)){
9326             return v + (defaultUnit || 'px');
9327         }
9328         return v;
9329     };
9330
9331     // special markup used throughout Roo when box wrapping elements
9332     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9333     /**
9334      * Visibility mode constant - Use visibility to hide element
9335      * @static
9336      * @type Number
9337      */
9338     El.VISIBILITY = 1;
9339     /**
9340      * Visibility mode constant - Use display to hide element
9341      * @static
9342      * @type Number
9343      */
9344     El.DISPLAY = 2;
9345
9346     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9347     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9348     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9349
9350
9351
9352     /**
9353      * @private
9354      */
9355     El.cache = {};
9356
9357     var docEl;
9358
9359     /**
9360      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9361      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9362      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9363      * @return {Element} The Element object
9364      * @static
9365      */
9366     El.get = function(el){
9367         var ex, elm, id;
9368         if(!el){ return null; }
9369         if(typeof el == "string"){ // element id
9370             if(!(elm = document.getElementById(el))){
9371                 return null;
9372             }
9373             if(ex = El.cache[el]){
9374                 ex.dom = elm;
9375             }else{
9376                 ex = El.cache[el] = new El(elm);
9377             }
9378             return ex;
9379         }else if(el.tagName){ // dom element
9380             if(!(id = el.id)){
9381                 id = Roo.id(el);
9382             }
9383             if(ex = El.cache[id]){
9384                 ex.dom = el;
9385             }else{
9386                 ex = El.cache[id] = new El(el);
9387             }
9388             return ex;
9389         }else if(el instanceof El){
9390             if(el != docEl){
9391                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9392                                                               // catch case where it hasn't been appended
9393                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9394             }
9395             return el;
9396         }else if(el.isComposite){
9397             return el;
9398         }else if(el instanceof Array){
9399             return El.select(el);
9400         }else if(el == document){
9401             // create a bogus element object representing the document object
9402             if(!docEl){
9403                 var f = function(){};
9404                 f.prototype = El.prototype;
9405                 docEl = new f();
9406                 docEl.dom = document;
9407             }
9408             return docEl;
9409         }
9410         return null;
9411     };
9412
9413     // private
9414     El.uncache = function(el){
9415         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9416             if(a[i]){
9417                 delete El.cache[a[i].id || a[i]];
9418             }
9419         }
9420     };
9421
9422     // private
9423     // Garbage collection - uncache elements/purge listeners on orphaned elements
9424     // so we don't hold a reference and cause the browser to retain them
9425     El.garbageCollect = function(){
9426         if(!Roo.enableGarbageCollector){
9427             clearInterval(El.collectorThread);
9428             return;
9429         }
9430         for(var eid in El.cache){
9431             var el = El.cache[eid], d = el.dom;
9432             // -------------------------------------------------------
9433             // Determining what is garbage:
9434             // -------------------------------------------------------
9435             // !d
9436             // dom node is null, definitely garbage
9437             // -------------------------------------------------------
9438             // !d.parentNode
9439             // no parentNode == direct orphan, definitely garbage
9440             // -------------------------------------------------------
9441             // !d.offsetParent && !document.getElementById(eid)
9442             // display none elements have no offsetParent so we will
9443             // also try to look it up by it's id. However, check
9444             // offsetParent first so we don't do unneeded lookups.
9445             // This enables collection of elements that are not orphans
9446             // directly, but somewhere up the line they have an orphan
9447             // parent.
9448             // -------------------------------------------------------
9449             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9450                 delete El.cache[eid];
9451                 if(d && Roo.enableListenerCollection){
9452                     E.purgeElement(d);
9453                 }
9454             }
9455         }
9456     }
9457     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9458
9459
9460     // dom is optional
9461     El.Flyweight = function(dom){
9462         this.dom = dom;
9463     };
9464     El.Flyweight.prototype = El.prototype;
9465
9466     El._flyweights = {};
9467     /**
9468      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9469      * the dom node can be overwritten by other code.
9470      * @param {String/HTMLElement} el The dom node or id
9471      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9472      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9473      * @static
9474      * @return {Element} The shared Element object
9475      */
9476     El.fly = function(el, named){
9477         named = named || '_global';
9478         el = Roo.getDom(el);
9479         if(!el){
9480             return null;
9481         }
9482         if(!El._flyweights[named]){
9483             El._flyweights[named] = new El.Flyweight();
9484         }
9485         El._flyweights[named].dom = el;
9486         return El._flyweights[named];
9487     };
9488
9489     /**
9490      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9491      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9492      * Shorthand of {@link Roo.Element#get}
9493      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9494      * @return {Element} The Element object
9495      * @member Roo
9496      * @method get
9497      */
9498     Roo.get = El.get;
9499     /**
9500      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9501      * the dom node can be overwritten by other code.
9502      * Shorthand of {@link Roo.Element#fly}
9503      * @param {String/HTMLElement} el The dom node or id
9504      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9505      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9506      * @static
9507      * @return {Element} The shared Element object
9508      * @member Roo
9509      * @method fly
9510      */
9511     Roo.fly = El.fly;
9512
9513     // speedy lookup for elements never to box adjust
9514     var noBoxAdjust = Roo.isStrict ? {
9515         select:1
9516     } : {
9517         input:1, select:1, textarea:1
9518     };
9519     if(Roo.isIE || Roo.isGecko){
9520         noBoxAdjust['button'] = 1;
9521     }
9522
9523
9524     Roo.EventManager.on(window, 'unload', function(){
9525         delete El.cache;
9526         delete El._flyweights;
9527     });
9528 })();
9529
9530
9531
9532
9533 if(Roo.DomQuery){
9534     Roo.Element.selectorFunction = Roo.DomQuery.select;
9535 }
9536
9537 Roo.Element.select = function(selector, unique, root){
9538     var els;
9539     if(typeof selector == "string"){
9540         els = Roo.Element.selectorFunction(selector, root);
9541     }else if(selector.length !== undefined){
9542         els = selector;
9543     }else{
9544         throw "Invalid selector";
9545     }
9546     if(unique === true){
9547         return new Roo.CompositeElement(els);
9548     }else{
9549         return new Roo.CompositeElementLite(els);
9550     }
9551 };
9552 /**
9553  * Selects elements based on the passed CSS selector to enable working on them as 1.
9554  * @param {String/Array} selector The CSS selector or an array of elements
9555  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9556  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9557  * @return {CompositeElementLite/CompositeElement}
9558  * @member Roo
9559  * @method select
9560  */
9561 Roo.select = Roo.Element.select;
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576 /*
9577  * Based on:
9578  * Ext JS Library 1.1.1
9579  * Copyright(c) 2006-2007, Ext JS, LLC.
9580  *
9581  * Originally Released Under LGPL - original licence link has changed is not relivant.
9582  *
9583  * Fork - LGPL
9584  * <script type="text/javascript">
9585  */
9586
9587
9588
9589 //Notifies Element that fx methods are available
9590 Roo.enableFx = true;
9591
9592 /**
9593  * @class Roo.Fx
9594  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9595  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9596  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9597  * Element effects to work.</p><br/>
9598  *
9599  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9600  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9601  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9602  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9603  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9604  * expected results and should be done with care.</p><br/>
9605  *
9606  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9607  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9608 <pre>
9609 Value  Description
9610 -----  -----------------------------
9611 tl     The top left corner
9612 t      The center of the top edge
9613 tr     The top right corner
9614 l      The center of the left edge
9615 r      The center of the right edge
9616 bl     The bottom left corner
9617 b      The center of the bottom edge
9618 br     The bottom right corner
9619 </pre>
9620  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9621  * below are common options that can be passed to any Fx method.</b>
9622  * @cfg {Function} callback A function called when the effect is finished
9623  * @cfg {Object} scope The scope of the effect function
9624  * @cfg {String} easing A valid Easing value for the effect
9625  * @cfg {String} afterCls A css class to apply after the effect
9626  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9627  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9628  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9629  * effects that end with the element being visually hidden, ignored otherwise)
9630  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9631  * a function which returns such a specification that will be applied to the Element after the effect finishes
9632  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9633  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9634  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9635  */
9636 Roo.Fx = {
9637         /**
9638          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9639          * origin for the slide effect.  This function automatically handles wrapping the element with
9640          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9641          * Usage:
9642          *<pre><code>
9643 // default: slide the element in from the top
9644 el.slideIn();
9645
9646 // custom: slide the element in from the right with a 2-second duration
9647 el.slideIn('r', { duration: 2 });
9648
9649 // common config options shown with default values
9650 el.slideIn('t', {
9651     easing: 'easeOut',
9652     duration: .5
9653 });
9654 </code></pre>
9655          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9656          * @param {Object} options (optional) Object literal with any of the Fx config options
9657          * @return {Roo.Element} The Element
9658          */
9659     slideIn : function(anchor, o){
9660         var el = this.getFxEl();
9661         o = o || {};
9662
9663         el.queueFx(o, function(){
9664
9665             anchor = anchor || "t";
9666
9667             // fix display to visibility
9668             this.fixDisplay();
9669
9670             // restore values after effect
9671             var r = this.getFxRestore();
9672             var b = this.getBox();
9673             // fixed size for slide
9674             this.setSize(b);
9675
9676             // wrap if needed
9677             var wrap = this.fxWrap(r.pos, o, "hidden");
9678
9679             var st = this.dom.style;
9680             st.visibility = "visible";
9681             st.position = "absolute";
9682
9683             // clear out temp styles after slide and unwrap
9684             var after = function(){
9685                 el.fxUnwrap(wrap, r.pos, o);
9686                 st.width = r.width;
9687                 st.height = r.height;
9688                 el.afterFx(o);
9689             };
9690             // time to calc the positions
9691             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9692
9693             switch(anchor.toLowerCase()){
9694                 case "t":
9695                     wrap.setSize(b.width, 0);
9696                     st.left = st.bottom = "0";
9697                     a = {height: bh};
9698                 break;
9699                 case "l":
9700                     wrap.setSize(0, b.height);
9701                     st.right = st.top = "0";
9702                     a = {width: bw};
9703                 break;
9704                 case "r":
9705                     wrap.setSize(0, b.height);
9706                     wrap.setX(b.right);
9707                     st.left = st.top = "0";
9708                     a = {width: bw, points: pt};
9709                 break;
9710                 case "b":
9711                     wrap.setSize(b.width, 0);
9712                     wrap.setY(b.bottom);
9713                     st.left = st.top = "0";
9714                     a = {height: bh, points: pt};
9715                 break;
9716                 case "tl":
9717                     wrap.setSize(0, 0);
9718                     st.right = st.bottom = "0";
9719                     a = {width: bw, height: bh};
9720                 break;
9721                 case "bl":
9722                     wrap.setSize(0, 0);
9723                     wrap.setY(b.y+b.height);
9724                     st.right = st.top = "0";
9725                     a = {width: bw, height: bh, points: pt};
9726                 break;
9727                 case "br":
9728                     wrap.setSize(0, 0);
9729                     wrap.setXY([b.right, b.bottom]);
9730                     st.left = st.top = "0";
9731                     a = {width: bw, height: bh, points: pt};
9732                 break;
9733                 case "tr":
9734                     wrap.setSize(0, 0);
9735                     wrap.setX(b.x+b.width);
9736                     st.left = st.bottom = "0";
9737                     a = {width: bw, height: bh, points: pt};
9738                 break;
9739             }
9740             this.dom.style.visibility = "visible";
9741             wrap.show();
9742
9743             arguments.callee.anim = wrap.fxanim(a,
9744                 o,
9745                 'motion',
9746                 .5,
9747                 'easeOut', after);
9748         });
9749         return this;
9750     },
9751     
9752         /**
9753          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9754          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9755          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9756          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9757          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9758          * Usage:
9759          *<pre><code>
9760 // default: slide the element out to the top
9761 el.slideOut();
9762
9763 // custom: slide the element out to the right with a 2-second duration
9764 el.slideOut('r', { duration: 2 });
9765
9766 // common config options shown with default values
9767 el.slideOut('t', {
9768     easing: 'easeOut',
9769     duration: .5,
9770     remove: false,
9771     useDisplay: false
9772 });
9773 </code></pre>
9774          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9775          * @param {Object} options (optional) Object literal with any of the Fx config options
9776          * @return {Roo.Element} The Element
9777          */
9778     slideOut : function(anchor, o){
9779         var el = this.getFxEl();
9780         o = o || {};
9781
9782         el.queueFx(o, function(){
9783
9784             anchor = anchor || "t";
9785
9786             // restore values after effect
9787             var r = this.getFxRestore();
9788             
9789             var b = this.getBox();
9790             // fixed size for slide
9791             this.setSize(b);
9792
9793             // wrap if needed
9794             var wrap = this.fxWrap(r.pos, o, "visible");
9795
9796             var st = this.dom.style;
9797             st.visibility = "visible";
9798             st.position = "absolute";
9799
9800             wrap.setSize(b);
9801
9802             var after = function(){
9803                 if(o.useDisplay){
9804                     el.setDisplayed(false);
9805                 }else{
9806                     el.hide();
9807                 }
9808
9809                 el.fxUnwrap(wrap, r.pos, o);
9810
9811                 st.width = r.width;
9812                 st.height = r.height;
9813
9814                 el.afterFx(o);
9815             };
9816
9817             var a, zero = {to: 0};
9818             switch(anchor.toLowerCase()){
9819                 case "t":
9820                     st.left = st.bottom = "0";
9821                     a = {height: zero};
9822                 break;
9823                 case "l":
9824                     st.right = st.top = "0";
9825                     a = {width: zero};
9826                 break;
9827                 case "r":
9828                     st.left = st.top = "0";
9829                     a = {width: zero, points: {to:[b.right, b.y]}};
9830                 break;
9831                 case "b":
9832                     st.left = st.top = "0";
9833                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9834                 break;
9835                 case "tl":
9836                     st.right = st.bottom = "0";
9837                     a = {width: zero, height: zero};
9838                 break;
9839                 case "bl":
9840                     st.right = st.top = "0";
9841                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9842                 break;
9843                 case "br":
9844                     st.left = st.top = "0";
9845                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9846                 break;
9847                 case "tr":
9848                     st.left = st.bottom = "0";
9849                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9850                 break;
9851             }
9852
9853             arguments.callee.anim = wrap.fxanim(a,
9854                 o,
9855                 'motion',
9856                 .5,
9857                 "easeOut", after);
9858         });
9859         return this;
9860     },
9861
9862         /**
9863          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9864          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9865          * The element must be removed from the DOM using the 'remove' config option if desired.
9866          * Usage:
9867          *<pre><code>
9868 // default
9869 el.puff();
9870
9871 // common config options shown with default values
9872 el.puff({
9873     easing: 'easeOut',
9874     duration: .5,
9875     remove: false,
9876     useDisplay: false
9877 });
9878 </code></pre>
9879          * @param {Object} options (optional) Object literal with any of the Fx config options
9880          * @return {Roo.Element} The Element
9881          */
9882     puff : function(o){
9883         var el = this.getFxEl();
9884         o = o || {};
9885
9886         el.queueFx(o, function(){
9887             this.clearOpacity();
9888             this.show();
9889
9890             // restore values after effect
9891             var r = this.getFxRestore();
9892             var st = this.dom.style;
9893
9894             var after = function(){
9895                 if(o.useDisplay){
9896                     el.setDisplayed(false);
9897                 }else{
9898                     el.hide();
9899                 }
9900
9901                 el.clearOpacity();
9902
9903                 el.setPositioning(r.pos);
9904                 st.width = r.width;
9905                 st.height = r.height;
9906                 st.fontSize = '';
9907                 el.afterFx(o);
9908             };
9909
9910             var width = this.getWidth();
9911             var height = this.getHeight();
9912
9913             arguments.callee.anim = this.fxanim({
9914                     width : {to: this.adjustWidth(width * 2)},
9915                     height : {to: this.adjustHeight(height * 2)},
9916                     points : {by: [-(width * .5), -(height * .5)]},
9917                     opacity : {to: 0},
9918                     fontSize: {to:200, unit: "%"}
9919                 },
9920                 o,
9921                 'motion',
9922                 .5,
9923                 "easeOut", after);
9924         });
9925         return this;
9926     },
9927
9928         /**
9929          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9930          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9931          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9932          * Usage:
9933          *<pre><code>
9934 // default
9935 el.switchOff();
9936
9937 // all config options shown with default values
9938 el.switchOff({
9939     easing: 'easeIn',
9940     duration: .3,
9941     remove: false,
9942     useDisplay: false
9943 });
9944 </code></pre>
9945          * @param {Object} options (optional) Object literal with any of the Fx config options
9946          * @return {Roo.Element} The Element
9947          */
9948     switchOff : function(o){
9949         var el = this.getFxEl();
9950         o = o || {};
9951
9952         el.queueFx(o, function(){
9953             this.clearOpacity();
9954             this.clip();
9955
9956             // restore values after effect
9957             var r = this.getFxRestore();
9958             var st = this.dom.style;
9959
9960             var after = function(){
9961                 if(o.useDisplay){
9962                     el.setDisplayed(false);
9963                 }else{
9964                     el.hide();
9965                 }
9966
9967                 el.clearOpacity();
9968                 el.setPositioning(r.pos);
9969                 st.width = r.width;
9970                 st.height = r.height;
9971
9972                 el.afterFx(o);
9973             };
9974
9975             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
9976                 this.clearOpacity();
9977                 (function(){
9978                     this.fxanim({
9979                         height:{to:1},
9980                         points:{by:[0, this.getHeight() * .5]}
9981                     }, o, 'motion', 0.3, 'easeIn', after);
9982                 }).defer(100, this);
9983             });
9984         });
9985         return this;
9986     },
9987
9988     /**
9989      * Highlights the Element by setting a color (applies to the background-color by default, but can be
9990      * changed using the "attr" config option) and then fading back to the original color. If no original
9991      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
9992      * Usage:
9993 <pre><code>
9994 // default: highlight background to yellow
9995 el.highlight();
9996
9997 // custom: highlight foreground text to blue for 2 seconds
9998 el.highlight("0000ff", { attr: 'color', duration: 2 });
9999
10000 // common config options shown with default values
10001 el.highlight("ffff9c", {
10002     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10003     endColor: (current color) or "ffffff",
10004     easing: 'easeIn',
10005     duration: 1
10006 });
10007 </code></pre>
10008      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10009      * @param {Object} options (optional) Object literal with any of the Fx config options
10010      * @return {Roo.Element} The Element
10011      */ 
10012     highlight : function(color, o){
10013         var el = this.getFxEl();
10014         o = o || {};
10015
10016         el.queueFx(o, function(){
10017             color = color || "ffff9c";
10018             attr = o.attr || "backgroundColor";
10019
10020             this.clearOpacity();
10021             this.show();
10022
10023             var origColor = this.getColor(attr);
10024             var restoreColor = this.dom.style[attr];
10025             endColor = (o.endColor || origColor) || "ffffff";
10026
10027             var after = function(){
10028                 el.dom.style[attr] = restoreColor;
10029                 el.afterFx(o);
10030             };
10031
10032             var a = {};
10033             a[attr] = {from: color, to: endColor};
10034             arguments.callee.anim = this.fxanim(a,
10035                 o,
10036                 'color',
10037                 1,
10038                 'easeIn', after);
10039         });
10040         return this;
10041     },
10042
10043    /**
10044     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10045     * Usage:
10046 <pre><code>
10047 // default: a single light blue ripple
10048 el.frame();
10049
10050 // custom: 3 red ripples lasting 3 seconds total
10051 el.frame("ff0000", 3, { duration: 3 });
10052
10053 // common config options shown with default values
10054 el.frame("C3DAF9", 1, {
10055     duration: 1 //duration of entire animation (not each individual ripple)
10056     // Note: Easing is not configurable and will be ignored if included
10057 });
10058 </code></pre>
10059     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10060     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10061     * @param {Object} options (optional) Object literal with any of the Fx config options
10062     * @return {Roo.Element} The Element
10063     */
10064     frame : function(color, count, o){
10065         var el = this.getFxEl();
10066         o = o || {};
10067
10068         el.queueFx(o, function(){
10069             color = color || "#C3DAF9";
10070             if(color.length == 6){
10071                 color = "#" + color;
10072             }
10073             count = count || 1;
10074             duration = o.duration || 1;
10075             this.show();
10076
10077             var b = this.getBox();
10078             var animFn = function(){
10079                 var proxy = this.createProxy({
10080
10081                      style:{
10082                         visbility:"hidden",
10083                         position:"absolute",
10084                         "z-index":"35000", // yee haw
10085                         border:"0px solid " + color
10086                      }
10087                   });
10088                 var scale = Roo.isBorderBox ? 2 : 1;
10089                 proxy.animate({
10090                     top:{from:b.y, to:b.y - 20},
10091                     left:{from:b.x, to:b.x - 20},
10092                     borderWidth:{from:0, to:10},
10093                     opacity:{from:1, to:0},
10094                     height:{from:b.height, to:(b.height + (20*scale))},
10095                     width:{from:b.width, to:(b.width + (20*scale))}
10096                 }, duration, function(){
10097                     proxy.remove();
10098                 });
10099                 if(--count > 0){
10100                      animFn.defer((duration/2)*1000, this);
10101                 }else{
10102                     el.afterFx(o);
10103                 }
10104             };
10105             animFn.call(this);
10106         });
10107         return this;
10108     },
10109
10110    /**
10111     * Creates a pause before any subsequent queued effects begin.  If there are
10112     * no effects queued after the pause it will have no effect.
10113     * Usage:
10114 <pre><code>
10115 el.pause(1);
10116 </code></pre>
10117     * @param {Number} seconds The length of time to pause (in seconds)
10118     * @return {Roo.Element} The Element
10119     */
10120     pause : function(seconds){
10121         var el = this.getFxEl();
10122         var o = {};
10123
10124         el.queueFx(o, function(){
10125             setTimeout(function(){
10126                 el.afterFx(o);
10127             }, seconds * 1000);
10128         });
10129         return this;
10130     },
10131
10132    /**
10133     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10134     * using the "endOpacity" config option.
10135     * Usage:
10136 <pre><code>
10137 // default: fade in from opacity 0 to 100%
10138 el.fadeIn();
10139
10140 // custom: fade in from opacity 0 to 75% over 2 seconds
10141 el.fadeIn({ endOpacity: .75, duration: 2});
10142
10143 // common config options shown with default values
10144 el.fadeIn({
10145     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10146     easing: 'easeOut',
10147     duration: .5
10148 });
10149 </code></pre>
10150     * @param {Object} options (optional) Object literal with any of the Fx config options
10151     * @return {Roo.Element} The Element
10152     */
10153     fadeIn : function(o){
10154         var el = this.getFxEl();
10155         o = o || {};
10156         el.queueFx(o, function(){
10157             this.setOpacity(0);
10158             this.fixDisplay();
10159             this.dom.style.visibility = 'visible';
10160             var to = o.endOpacity || 1;
10161             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10162                 o, null, .5, "easeOut", function(){
10163                 if(to == 1){
10164                     this.clearOpacity();
10165                 }
10166                 el.afterFx(o);
10167             });
10168         });
10169         return this;
10170     },
10171
10172    /**
10173     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10174     * using the "endOpacity" config option.
10175     * Usage:
10176 <pre><code>
10177 // default: fade out from the element's current opacity to 0
10178 el.fadeOut();
10179
10180 // custom: fade out from the element's current opacity to 25% over 2 seconds
10181 el.fadeOut({ endOpacity: .25, duration: 2});
10182
10183 // common config options shown with default values
10184 el.fadeOut({
10185     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10186     easing: 'easeOut',
10187     duration: .5
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192     * @param {Object} options (optional) Object literal with any of the Fx config options
10193     * @return {Roo.Element} The Element
10194     */
10195     fadeOut : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198         el.queueFx(o, function(){
10199             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10200                 o, null, .5, "easeOut", function(){
10201                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10202                      this.dom.style.display = "none";
10203                 }else{
10204                      this.dom.style.visibility = "hidden";
10205                 }
10206                 this.clearOpacity();
10207                 el.afterFx(o);
10208             });
10209         });
10210         return this;
10211     },
10212
10213    /**
10214     * Animates the transition of an element's dimensions from a starting height/width
10215     * to an ending height/width.
10216     * Usage:
10217 <pre><code>
10218 // change height and width to 100x100 pixels
10219 el.scale(100, 100);
10220
10221 // common config options shown with default values.  The height and width will default to
10222 // the element's existing values if passed as null.
10223 el.scale(
10224     [element's width],
10225     [element's height], {
10226     easing: 'easeOut',
10227     duration: .35
10228 });
10229 </code></pre>
10230     * @param {Number} width  The new width (pass undefined to keep the original width)
10231     * @param {Number} height  The new height (pass undefined to keep the original height)
10232     * @param {Object} options (optional) Object literal with any of the Fx config options
10233     * @return {Roo.Element} The Element
10234     */
10235     scale : function(w, h, o){
10236         this.shift(Roo.apply({}, o, {
10237             width: w,
10238             height: h
10239         }));
10240         return this;
10241     },
10242
10243    /**
10244     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10245     * Any of these properties not specified in the config object will not be changed.  This effect 
10246     * requires that at least one new dimension, position or opacity setting must be passed in on
10247     * the config object in order for the function to have any effect.
10248     * Usage:
10249 <pre><code>
10250 // slide the element horizontally to x position 200 while changing the height and opacity
10251 el.shift({ x: 200, height: 50, opacity: .8 });
10252
10253 // common config options shown with default values.
10254 el.shift({
10255     width: [element's width],
10256     height: [element's height],
10257     x: [element's x position],
10258     y: [element's y position],
10259     opacity: [element's opacity],
10260     easing: 'easeOut',
10261     duration: .35
10262 });
10263 </code></pre>
10264     * @param {Object} options  Object literal with any of the Fx config options
10265     * @return {Roo.Element} The Element
10266     */
10267     shift : function(o){
10268         var el = this.getFxEl();
10269         o = o || {};
10270         el.queueFx(o, function(){
10271             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10272             if(w !== undefined){
10273                 a.width = {to: this.adjustWidth(w)};
10274             }
10275             if(h !== undefined){
10276                 a.height = {to: this.adjustHeight(h)};
10277             }
10278             if(x !== undefined || y !== undefined){
10279                 a.points = {to: [
10280                     x !== undefined ? x : this.getX(),
10281                     y !== undefined ? y : this.getY()
10282                 ]};
10283             }
10284             if(op !== undefined){
10285                 a.opacity = {to: op};
10286             }
10287             if(o.xy !== undefined){
10288                 a.points = {to: o.xy};
10289             }
10290             arguments.callee.anim = this.fxanim(a,
10291                 o, 'motion', .35, "easeOut", function(){
10292                 el.afterFx(o);
10293             });
10294         });
10295         return this;
10296     },
10297
10298         /**
10299          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10300          * ending point of the effect.
10301          * Usage:
10302          *<pre><code>
10303 // default: slide the element downward while fading out
10304 el.ghost();
10305
10306 // custom: slide the element out to the right with a 2-second duration
10307 el.ghost('r', { duration: 2 });
10308
10309 // common config options shown with default values
10310 el.ghost('b', {
10311     easing: 'easeOut',
10312     duration: .5
10313     remove: false,
10314     useDisplay: false
10315 });
10316 </code></pre>
10317          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10318          * @param {Object} options (optional) Object literal with any of the Fx config options
10319          * @return {Roo.Element} The Element
10320          */
10321     ghost : function(anchor, o){
10322         var el = this.getFxEl();
10323         o = o || {};
10324
10325         el.queueFx(o, function(){
10326             anchor = anchor || "b";
10327
10328             // restore values after effect
10329             var r = this.getFxRestore();
10330             var w = this.getWidth(),
10331                 h = this.getHeight();
10332
10333             var st = this.dom.style;
10334
10335             var after = function(){
10336                 if(o.useDisplay){
10337                     el.setDisplayed(false);
10338                 }else{
10339                     el.hide();
10340                 }
10341
10342                 el.clearOpacity();
10343                 el.setPositioning(r.pos);
10344                 st.width = r.width;
10345                 st.height = r.height;
10346
10347                 el.afterFx(o);
10348             };
10349
10350             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10351             switch(anchor.toLowerCase()){
10352                 case "t":
10353                     pt.by = [0, -h];
10354                 break;
10355                 case "l":
10356                     pt.by = [-w, 0];
10357                 break;
10358                 case "r":
10359                     pt.by = [w, 0];
10360                 break;
10361                 case "b":
10362                     pt.by = [0, h];
10363                 break;
10364                 case "tl":
10365                     pt.by = [-w, -h];
10366                 break;
10367                 case "bl":
10368                     pt.by = [-w, h];
10369                 break;
10370                 case "br":
10371                     pt.by = [w, h];
10372                 break;
10373                 case "tr":
10374                     pt.by = [w, -h];
10375                 break;
10376             }
10377
10378             arguments.callee.anim = this.fxanim(a,
10379                 o,
10380                 'motion',
10381                 .5,
10382                 "easeOut", after);
10383         });
10384         return this;
10385     },
10386
10387         /**
10388          * Ensures that all effects queued after syncFx is called on the element are
10389          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10390          * @return {Roo.Element} The Element
10391          */
10392     syncFx : function(){
10393         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10394             block : false,
10395             concurrent : true,
10396             stopFx : false
10397         });
10398         return this;
10399     },
10400
10401         /**
10402          * Ensures that all effects queued after sequenceFx is called on the element are
10403          * run in sequence.  This is the opposite of {@link #syncFx}.
10404          * @return {Roo.Element} The Element
10405          */
10406     sequenceFx : function(){
10407         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10408             block : false,
10409             concurrent : false,
10410             stopFx : false
10411         });
10412         return this;
10413     },
10414
10415         /* @private */
10416     nextFx : function(){
10417         var ef = this.fxQueue[0];
10418         if(ef){
10419             ef.call(this);
10420         }
10421     },
10422
10423         /**
10424          * Returns true if the element has any effects actively running or queued, else returns false.
10425          * @return {Boolean} True if element has active effects, else false
10426          */
10427     hasActiveFx : function(){
10428         return this.fxQueue && this.fxQueue[0];
10429     },
10430
10431         /**
10432          * Stops any running effects and clears the element's internal effects queue if it contains
10433          * any additional effects that haven't started yet.
10434          * @return {Roo.Element} The Element
10435          */
10436     stopFx : function(){
10437         if(this.hasActiveFx()){
10438             var cur = this.fxQueue[0];
10439             if(cur && cur.anim && cur.anim.isAnimated()){
10440                 this.fxQueue = [cur]; // clear out others
10441                 cur.anim.stop(true);
10442             }
10443         }
10444         return this;
10445     },
10446
10447         /* @private */
10448     beforeFx : function(o){
10449         if(this.hasActiveFx() && !o.concurrent){
10450            if(o.stopFx){
10451                this.stopFx();
10452                return true;
10453            }
10454            return false;
10455         }
10456         return true;
10457     },
10458
10459         /**
10460          * Returns true if the element is currently blocking so that no other effect can be queued
10461          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10462          * used to ensure that an effect initiated by a user action runs to completion prior to the
10463          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10464          * @return {Boolean} True if blocking, else false
10465          */
10466     hasFxBlock : function(){
10467         var q = this.fxQueue;
10468         return q && q[0] && q[0].block;
10469     },
10470
10471         /* @private */
10472     queueFx : function(o, fn){
10473         if(!this.fxQueue){
10474             this.fxQueue = [];
10475         }
10476         if(!this.hasFxBlock()){
10477             Roo.applyIf(o, this.fxDefaults);
10478             if(!o.concurrent){
10479                 var run = this.beforeFx(o);
10480                 fn.block = o.block;
10481                 this.fxQueue.push(fn);
10482                 if(run){
10483                     this.nextFx();
10484                 }
10485             }else{
10486                 fn.call(this);
10487             }
10488         }
10489         return this;
10490     },
10491
10492         /* @private */
10493     fxWrap : function(pos, o, vis){
10494         var wrap;
10495         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10496             var wrapXY;
10497             if(o.fixPosition){
10498                 wrapXY = this.getXY();
10499             }
10500             var div = document.createElement("div");
10501             div.style.visibility = vis;
10502             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10503             wrap.setPositioning(pos);
10504             if(wrap.getStyle("position") == "static"){
10505                 wrap.position("relative");
10506             }
10507             this.clearPositioning('auto');
10508             wrap.clip();
10509             wrap.dom.appendChild(this.dom);
10510             if(wrapXY){
10511                 wrap.setXY(wrapXY);
10512             }
10513         }
10514         return wrap;
10515     },
10516
10517         /* @private */
10518     fxUnwrap : function(wrap, pos, o){
10519         this.clearPositioning();
10520         this.setPositioning(pos);
10521         if(!o.wrap){
10522             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10523             wrap.remove();
10524         }
10525     },
10526
10527         /* @private */
10528     getFxRestore : function(){
10529         var st = this.dom.style;
10530         return {pos: this.getPositioning(), width: st.width, height : st.height};
10531     },
10532
10533         /* @private */
10534     afterFx : function(o){
10535         if(o.afterStyle){
10536             this.applyStyles(o.afterStyle);
10537         }
10538         if(o.afterCls){
10539             this.addClass(o.afterCls);
10540         }
10541         if(o.remove === true){
10542             this.remove();
10543         }
10544         Roo.callback(o.callback, o.scope, [this]);
10545         if(!o.concurrent){
10546             this.fxQueue.shift();
10547             this.nextFx();
10548         }
10549     },
10550
10551         /* @private */
10552     getFxEl : function(){ // support for composite element fx
10553         return Roo.get(this.dom);
10554     },
10555
10556         /* @private */
10557     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10558         animType = animType || 'run';
10559         opt = opt || {};
10560         var anim = Roo.lib.Anim[animType](
10561             this.dom, args,
10562             (opt.duration || defaultDur) || .35,
10563             (opt.easing || defaultEase) || 'easeOut',
10564             function(){
10565                 Roo.callback(cb, this);
10566             },
10567             this
10568         );
10569         opt.anim = anim;
10570         return anim;
10571     }
10572 };
10573
10574 // backwords compat
10575 Roo.Fx.resize = Roo.Fx.scale;
10576
10577 //When included, Roo.Fx is automatically applied to Element so that all basic
10578 //effects are available directly via the Element API
10579 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589
10590
10591 /**
10592  * @class Roo.CompositeElement
10593  * Standard composite class. Creates a Roo.Element for every element in the collection.
10594  * <br><br>
10595  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10596  * actions will be performed on all the elements in this collection.</b>
10597  * <br><br>
10598  * All methods return <i>this</i> and can be chained.
10599  <pre><code>
10600  var els = Roo.select("#some-el div.some-class", true);
10601  // or select directly from an existing element
10602  var el = Roo.get('some-el');
10603  el.select('div.some-class', true);
10604
10605  els.setWidth(100); // all elements become 100 width
10606  els.hide(true); // all elements fade out and hide
10607  // or
10608  els.setWidth(100).hide(true);
10609  </code></pre>
10610  */
10611 Roo.CompositeElement = function(els){
10612     this.elements = [];
10613     this.addElements(els);
10614 };
10615 Roo.CompositeElement.prototype = {
10616     isComposite: true,
10617     addElements : function(els){
10618         if(!els) return this;
10619         if(typeof els == "string"){
10620             els = Roo.Element.selectorFunction(els);
10621         }
10622         var yels = this.elements;
10623         var index = yels.length-1;
10624         for(var i = 0, len = els.length; i < len; i++) {
10625                 yels[++index] = Roo.get(els[i]);
10626         }
10627         return this;
10628     },
10629
10630     /**
10631     * Clears this composite and adds the elements returned by the passed selector.
10632     * @param {String/Array} els A string CSS selector, an array of elements or an element
10633     * @return {CompositeElement} this
10634     */
10635     fill : function(els){
10636         this.elements = [];
10637         this.add(els);
10638         return this;
10639     },
10640
10641     /**
10642     * Filters this composite to only elements that match the passed selector.
10643     * @param {String} selector A string CSS selector
10644     * @return {CompositeElement} this
10645     */
10646     filter : function(selector){
10647         var els = [];
10648         this.each(function(el){
10649             if(el.is(selector)){
10650                 els[els.length] = el.dom;
10651             }
10652         });
10653         this.fill(els);
10654         return this;
10655     },
10656
10657     invoke : function(fn, args){
10658         var els = this.elements;
10659         for(var i = 0, len = els.length; i < len; i++) {
10660                 Roo.Element.prototype[fn].apply(els[i], args);
10661         }
10662         return this;
10663     },
10664     /**
10665     * Adds elements to this composite.
10666     * @param {String/Array} els A string CSS selector, an array of elements or an element
10667     * @return {CompositeElement} this
10668     */
10669     add : function(els){
10670         if(typeof els == "string"){
10671             this.addElements(Roo.Element.selectorFunction(els));
10672         }else if(els.length !== undefined){
10673             this.addElements(els);
10674         }else{
10675             this.addElements([els]);
10676         }
10677         return this;
10678     },
10679     /**
10680     * Calls the passed function passing (el, this, index) for each element in this composite.
10681     * @param {Function} fn The function to call
10682     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10683     * @return {CompositeElement} this
10684     */
10685     each : function(fn, scope){
10686         var els = this.elements;
10687         for(var i = 0, len = els.length; i < len; i++){
10688             if(fn.call(scope || els[i], els[i], this, i) === false) {
10689                 break;
10690             }
10691         }
10692         return this;
10693     },
10694
10695     /**
10696      * Returns the Element object at the specified index
10697      * @param {Number} index
10698      * @return {Roo.Element}
10699      */
10700     item : function(index){
10701         return this.elements[index] || null;
10702     },
10703
10704     /**
10705      * Returns the first Element
10706      * @return {Roo.Element}
10707      */
10708     first : function(){
10709         return this.item(0);
10710     },
10711
10712     /**
10713      * Returns the last Element
10714      * @return {Roo.Element}
10715      */
10716     last : function(){
10717         return this.item(this.elements.length-1);
10718     },
10719
10720     /**
10721      * Returns the number of elements in this composite
10722      * @return Number
10723      */
10724     getCount : function(){
10725         return this.elements.length;
10726     },
10727
10728     /**
10729      * Returns true if this composite contains the passed element
10730      * @return Boolean
10731      */
10732     contains : function(el){
10733         return this.indexOf(el) !== -1;
10734     },
10735
10736     /**
10737      * Returns true if this composite contains the passed element
10738      * @return Boolean
10739      */
10740     indexOf : function(el){
10741         return this.elements.indexOf(Roo.get(el));
10742     },
10743
10744
10745     /**
10746     * Removes the specified element(s).
10747     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10748     * or an array of any of those.
10749     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10750     * @return {CompositeElement} this
10751     */
10752     removeElement : function(el, removeDom){
10753         if(el instanceof Array){
10754             for(var i = 0, len = el.length; i < len; i++){
10755                 this.removeElement(el[i]);
10756             }
10757             return this;
10758         }
10759         var index = typeof el == 'number' ? el : this.indexOf(el);
10760         if(index !== -1){
10761             if(removeDom){
10762                 var d = this.elements[index];
10763                 if(d.dom){
10764                     d.remove();
10765                 }else{
10766                     d.parentNode.removeChild(d);
10767                 }
10768             }
10769             this.elements.splice(index, 1);
10770         }
10771         return this;
10772     },
10773
10774     /**
10775     * Replaces the specified element with the passed element.
10776     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10777     * to replace.
10778     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10779     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10780     * @return {CompositeElement} this
10781     */
10782     replaceElement : function(el, replacement, domReplace){
10783         var index = typeof el == 'number' ? el : this.indexOf(el);
10784         if(index !== -1){
10785             if(domReplace){
10786                 this.elements[index].replaceWith(replacement);
10787             }else{
10788                 this.elements.splice(index, 1, Roo.get(replacement))
10789             }
10790         }
10791         return this;
10792     },
10793
10794     /**
10795      * Removes all elements.
10796      */
10797     clear : function(){
10798         this.elements = [];
10799     }
10800 };
10801 (function(){
10802     Roo.CompositeElement.createCall = function(proto, fnName){
10803         if(!proto[fnName]){
10804             proto[fnName] = function(){
10805                 return this.invoke(fnName, arguments);
10806             };
10807         }
10808     };
10809     for(var fnName in Roo.Element.prototype){
10810         if(typeof Roo.Element.prototype[fnName] == "function"){
10811             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10812         }
10813     };
10814 })();
10815 /*
10816  * Based on:
10817  * Ext JS Library 1.1.1
10818  * Copyright(c) 2006-2007, Ext JS, LLC.
10819  *
10820  * Originally Released Under LGPL - original licence link has changed is not relivant.
10821  *
10822  * Fork - LGPL
10823  * <script type="text/javascript">
10824  */
10825
10826 /**
10827  * @class Roo.CompositeElementLite
10828  * @extends Roo.CompositeElement
10829  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10830  <pre><code>
10831  var els = Roo.select("#some-el div.some-class");
10832  // or select directly from an existing element
10833  var el = Roo.get('some-el');
10834  el.select('div.some-class');
10835
10836  els.setWidth(100); // all elements become 100 width
10837  els.hide(true); // all elements fade out and hide
10838  // or
10839  els.setWidth(100).hide(true);
10840  </code></pre><br><br>
10841  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10842  * actions will be performed on all the elements in this collection.</b>
10843  */
10844 Roo.CompositeElementLite = function(els){
10845     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10846     this.el = new Roo.Element.Flyweight();
10847 };
10848 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10849     addElements : function(els){
10850         if(els){
10851             if(els instanceof Array){
10852                 this.elements = this.elements.concat(els);
10853             }else{
10854                 var yels = this.elements;
10855                 var index = yels.length-1;
10856                 for(var i = 0, len = els.length; i < len; i++) {
10857                     yels[++index] = els[i];
10858                 }
10859             }
10860         }
10861         return this;
10862     },
10863     invoke : function(fn, args){
10864         var els = this.elements;
10865         var el = this.el;
10866         for(var i = 0, len = els.length; i < len; i++) {
10867             el.dom = els[i];
10868                 Roo.Element.prototype[fn].apply(el, args);
10869         }
10870         return this;
10871     },
10872     /**
10873      * Returns a flyweight Element of the dom element object at the specified index
10874      * @param {Number} index
10875      * @return {Roo.Element}
10876      */
10877     item : function(index){
10878         if(!this.elements[index]){
10879             return null;
10880         }
10881         this.el.dom = this.elements[index];
10882         return this.el;
10883     },
10884
10885     // fixes scope with flyweight
10886     addListener : function(eventName, handler, scope, opt){
10887         var els = this.elements;
10888         for(var i = 0, len = els.length; i < len; i++) {
10889             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10890         }
10891         return this;
10892     },
10893
10894     /**
10895     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10896     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10897     * a reference to the dom node, use el.dom.</b>
10898     * @param {Function} fn The function to call
10899     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10900     * @return {CompositeElement} this
10901     */
10902     each : function(fn, scope){
10903         var els = this.elements;
10904         var el = this.el;
10905         for(var i = 0, len = els.length; i < len; i++){
10906             el.dom = els[i];
10907                 if(fn.call(scope || el, el, this, i) === false){
10908                 break;
10909             }
10910         }
10911         return this;
10912     },
10913
10914     indexOf : function(el){
10915         return this.elements.indexOf(Roo.getDom(el));
10916     },
10917
10918     replaceElement : function(el, replacement, domReplace){
10919         var index = typeof el == 'number' ? el : this.indexOf(el);
10920         if(index !== -1){
10921             replacement = Roo.getDom(replacement);
10922             if(domReplace){
10923                 var d = this.elements[index];
10924                 d.parentNode.insertBefore(replacement, d);
10925                 d.parentNode.removeChild(d);
10926             }
10927             this.elements.splice(index, 1, replacement);
10928         }
10929         return this;
10930     }
10931 });
10932 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10933
10934 /*
10935  * Based on:
10936  * Ext JS Library 1.1.1
10937  * Copyright(c) 2006-2007, Ext JS, LLC.
10938  *
10939  * Originally Released Under LGPL - original licence link has changed is not relivant.
10940  *
10941  * Fork - LGPL
10942  * <script type="text/javascript">
10943  */
10944
10945  
10946
10947 /**
10948  * @class Roo.data.Connection
10949  * @extends Roo.util.Observable
10950  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10951  * either to a configured URL, or to a URL specified at request time.<br><br>
10952  * <p>
10953  * Requests made by this class are asynchronous, and will return immediately. No data from
10954  * the server will be available to the statement immediately following the {@link #request} call.
10955  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10956  * <p>
10957  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10958  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10959  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10960  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10961  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10962  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10963  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10964  * standard DOM methods.
10965  * @constructor
10966  * @param {Object} config a configuration object.
10967  */
10968 Roo.data.Connection = function(config){
10969     Roo.apply(this, config);
10970     this.addEvents({
10971         /**
10972          * @event beforerequest
10973          * Fires before a network request is made to retrieve a data object.
10974          * @param {Connection} conn This Connection object.
10975          * @param {Object} options The options config object passed to the {@link #request} method.
10976          */
10977         "beforerequest" : true,
10978         /**
10979          * @event requestcomplete
10980          * Fires if the request was successfully completed.
10981          * @param {Connection} conn This Connection object.
10982          * @param {Object} response The XHR object containing the response data.
10983          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10984          * @param {Object} options The options config object passed to the {@link #request} method.
10985          */
10986         "requestcomplete" : true,
10987         /**
10988          * @event requestexception
10989          * Fires if an error HTTP status was returned from the server.
10990          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
10991          * @param {Connection} conn This Connection object.
10992          * @param {Object} response The XHR object containing the response data.
10993          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10994          * @param {Object} options The options config object passed to the {@link #request} method.
10995          */
10996         "requestexception" : true
10997     });
10998     Roo.data.Connection.superclass.constructor.call(this);
10999 };
11000
11001 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11002     /**
11003      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11004      */
11005     /**
11006      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11007      * extra parameters to each request made by this object. (defaults to undefined)
11008      */
11009     /**
11010      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11011      *  to each request made by this object. (defaults to undefined)
11012      */
11013     /**
11014      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11015      */
11016     /**
11017      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11018      */
11019     timeout : 30000,
11020     /**
11021      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11022      * @type Boolean
11023      */
11024     autoAbort:false,
11025
11026     /**
11027      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11028      * @type Boolean
11029      */
11030     disableCaching: true,
11031
11032     /**
11033      * Sends an HTTP request to a remote server.
11034      * @param {Object} options An object which may contain the following properties:<ul>
11035      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11036      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11037      * request, a url encoded string or a function to call to get either.</li>
11038      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11039      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11040      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11041      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11042      * <li>options {Object} The parameter to the request call.</li>
11043      * <li>success {Boolean} True if the request succeeded.</li>
11044      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11045      * </ul></li>
11046      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11047      * The callback is passed the following parameters:<ul>
11048      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11049      * <li>options {Object} The parameter to the request call.</li>
11050      * </ul></li>
11051      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11052      * The callback is passed the following parameters:<ul>
11053      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11054      * <li>options {Object} The parameter to the request call.</li>
11055      * </ul></li>
11056      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11057      * for the callback function. Defaults to the browser window.</li>
11058      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11059      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11060      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11061      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11062      * params for the post data. Any params will be appended to the URL.</li>
11063      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11064      * </ul>
11065      * @return {Number} transactionId
11066      */
11067     request : function(o){
11068         if(this.fireEvent("beforerequest", this, o) !== false){
11069             var p = o.params;
11070
11071             if(typeof p == "function"){
11072                 p = p.call(o.scope||window, o);
11073             }
11074             if(typeof p == "object"){
11075                 p = Roo.urlEncode(o.params);
11076             }
11077             if(this.extraParams){
11078                 var extras = Roo.urlEncode(this.extraParams);
11079                 p = p ? (p + '&' + extras) : extras;
11080             }
11081
11082             var url = o.url || this.url;
11083             if(typeof url == 'function'){
11084                 url = url.call(o.scope||window, o);
11085             }
11086
11087             if(o.form){
11088                 var form = Roo.getDom(o.form);
11089                 url = url || form.action;
11090
11091                 var enctype = form.getAttribute("enctype");
11092                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11093                     return this.doFormUpload(o, p, url);
11094                 }
11095                 var f = Roo.lib.Ajax.serializeForm(form);
11096                 p = p ? (p + '&' + f) : f;
11097             }
11098
11099             var hs = o.headers;
11100             if(this.defaultHeaders){
11101                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11102                 if(!o.headers){
11103                     o.headers = hs;
11104                 }
11105             }
11106
11107             var cb = {
11108                 success: this.handleResponse,
11109                 failure: this.handleFailure,
11110                 scope: this,
11111                 argument: {options: o},
11112                 timeout : this.timeout
11113             };
11114
11115             var method = o.method||this.method||(p ? "POST" : "GET");
11116
11117             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11118                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11119             }
11120
11121             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11122                 if(o.autoAbort){
11123                     this.abort();
11124                 }
11125             }else if(this.autoAbort !== false){
11126                 this.abort();
11127             }
11128
11129             if((method == 'GET' && p) || o.xmlData){
11130                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11131                 p = '';
11132             }
11133             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11134             return this.transId;
11135         }else{
11136             Roo.callback(o.callback, o.scope, [o, null, null]);
11137             return null;
11138         }
11139     },
11140
11141     /**
11142      * Determine whether this object has a request outstanding.
11143      * @param {Number} transactionId (Optional) defaults to the last transaction
11144      * @return {Boolean} True if there is an outstanding request.
11145      */
11146     isLoading : function(transId){
11147         if(transId){
11148             return Roo.lib.Ajax.isCallInProgress(transId);
11149         }else{
11150             return this.transId ? true : false;
11151         }
11152     },
11153
11154     /**
11155      * Aborts any outstanding request.
11156      * @param {Number} transactionId (Optional) defaults to the last transaction
11157      */
11158     abort : function(transId){
11159         if(transId || this.isLoading()){
11160             Roo.lib.Ajax.abort(transId || this.transId);
11161         }
11162     },
11163
11164     // private
11165     handleResponse : function(response){
11166         this.transId = false;
11167         var options = response.argument.options;
11168         response.argument = options ? options.argument : null;
11169         this.fireEvent("requestcomplete", this, response, options);
11170         Roo.callback(options.success, options.scope, [response, options]);
11171         Roo.callback(options.callback, options.scope, [options, true, response]);
11172     },
11173
11174     // private
11175     handleFailure : function(response, e){
11176         this.transId = false;
11177         var options = response.argument.options;
11178         response.argument = options ? options.argument : null;
11179         this.fireEvent("requestexception", this, response, options, e);
11180         Roo.callback(options.failure, options.scope, [response, options]);
11181         Roo.callback(options.callback, options.scope, [options, false, response]);
11182     },
11183
11184     // private
11185     doFormUpload : function(o, ps, url){
11186         var id = Roo.id();
11187         var frame = document.createElement('iframe');
11188         frame.id = id;
11189         frame.name = id;
11190         frame.className = 'x-hidden';
11191         if(Roo.isIE){
11192             frame.src = Roo.SSL_SECURE_URL;
11193         }
11194         document.body.appendChild(frame);
11195
11196         if(Roo.isIE){
11197            document.frames[id].name = id;
11198         }
11199
11200         var form = Roo.getDom(o.form);
11201         form.target = id;
11202         form.method = 'POST';
11203         form.enctype = form.encoding = 'multipart/form-data';
11204         if(url){
11205             form.action = url;
11206         }
11207
11208         var hiddens, hd;
11209         if(ps){ // add dynamic params
11210             hiddens = [];
11211             ps = Roo.urlDecode(ps, false);
11212             for(var k in ps){
11213                 if(ps.hasOwnProperty(k)){
11214                     hd = document.createElement('input');
11215                     hd.type = 'hidden';
11216                     hd.name = k;
11217                     hd.value = ps[k];
11218                     form.appendChild(hd);
11219                     hiddens.push(hd);
11220                 }
11221             }
11222         }
11223
11224         function cb(){
11225             var r = {  // bogus response object
11226                 responseText : '',
11227                 responseXML : null
11228             };
11229
11230             r.argument = o ? o.argument : null;
11231
11232             try { //
11233                 var doc;
11234                 if(Roo.isIE){
11235                     doc = frame.contentWindow.document;
11236                 }else {
11237                     doc = (frame.contentDocument || window.frames[id].document);
11238                 }
11239                 if(doc && doc.body){
11240                     r.responseText = doc.body.innerHTML;
11241                 }
11242                 if(doc && doc.XMLDocument){
11243                     r.responseXML = doc.XMLDocument;
11244                 }else {
11245                     r.responseXML = doc;
11246                 }
11247             }
11248             catch(e) {
11249                 // ignore
11250             }
11251
11252             Roo.EventManager.removeListener(frame, 'load', cb, this);
11253
11254             this.fireEvent("requestcomplete", this, r, o);
11255             Roo.callback(o.success, o.scope, [r, o]);
11256             Roo.callback(o.callback, o.scope, [o, true, r]);
11257
11258             setTimeout(function(){document.body.removeChild(frame);}, 100);
11259         }
11260
11261         Roo.EventManager.on(frame, 'load', cb, this);
11262         form.submit();
11263
11264         if(hiddens){ // remove dynamic params
11265             for(var i = 0, len = hiddens.length; i < len; i++){
11266                 form.removeChild(hiddens[i]);
11267             }
11268         }
11269     }
11270 });
11271
11272 /**
11273  * @class Roo.Ajax
11274  * @extends Roo.data.Connection
11275  * Global Ajax request class.
11276  *
11277  * @singleton
11278  */
11279 Roo.Ajax = new Roo.data.Connection({
11280     // fix up the docs
11281    /**
11282      * @cfg {String} url @hide
11283      */
11284     /**
11285      * @cfg {Object} extraParams @hide
11286      */
11287     /**
11288      * @cfg {Object} defaultHeaders @hide
11289      */
11290     /**
11291      * @cfg {String} method (Optional) @hide
11292      */
11293     /**
11294      * @cfg {Number} timeout (Optional) @hide
11295      */
11296     /**
11297      * @cfg {Boolean} autoAbort (Optional) @hide
11298      */
11299
11300     /**
11301      * @cfg {Boolean} disableCaching (Optional) @hide
11302      */
11303
11304     /**
11305      * @property  disableCaching
11306      * True to add a unique cache-buster param to GET requests. (defaults to true)
11307      * @type Boolean
11308      */
11309     /**
11310      * @property  url
11311      * The default URL to be used for requests to the server. (defaults to undefined)
11312      * @type String
11313      */
11314     /**
11315      * @property  extraParams
11316      * An object containing properties which are used as
11317      * extra parameters to each request made by this object. (defaults to undefined)
11318      * @type Object
11319      */
11320     /**
11321      * @property  defaultHeaders
11322      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11323      * @type Object
11324      */
11325     /**
11326      * @property  method
11327      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11328      * @type String
11329      */
11330     /**
11331      * @property  timeout
11332      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11333      * @type Number
11334      */
11335
11336     /**
11337      * @property  autoAbort
11338      * Whether a new request should abort any pending requests. (defaults to false)
11339      * @type Boolean
11340      */
11341     autoAbort : false,
11342
11343     /**
11344      * Serialize the passed form into a url encoded string
11345      * @param {String/HTMLElement} form
11346      * @return {String}
11347      */
11348     serializeForm : function(form){
11349         return Roo.lib.Ajax.serializeForm(form);
11350     }
11351 });/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361  
11362 /**
11363  * @class Roo.Ajax
11364  * @extends Roo.data.Connection
11365  * Global Ajax request class.
11366  *
11367  * @instanceOf  Roo.data.Connection
11368  */
11369 Roo.Ajax = new Roo.data.Connection({
11370     // fix up the docs
11371     
11372     /**
11373      * fix up scoping
11374      * @scope Roo.Ajax
11375      */
11376     
11377    /**
11378      * @cfg {String} url @hide
11379      */
11380     /**
11381      * @cfg {Object} extraParams @hide
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders @hide
11385      */
11386     /**
11387      * @cfg {String} method (Optional) @hide
11388      */
11389     /**
11390      * @cfg {Number} timeout (Optional) @hide
11391      */
11392     /**
11393      * @cfg {Boolean} autoAbort (Optional) @hide
11394      */
11395
11396     /**
11397      * @cfg {Boolean} disableCaching (Optional) @hide
11398      */
11399
11400     /**
11401      * @property  disableCaching
11402      * True to add a unique cache-buster param to GET requests. (defaults to true)
11403      * @type Boolean
11404      */
11405     /**
11406      * @property  url
11407      * The default URL to be used for requests to the server. (defaults to undefined)
11408      * @type String
11409      */
11410     /**
11411      * @property  extraParams
11412      * An object containing properties which are used as
11413      * extra parameters to each request made by this object. (defaults to undefined)
11414      * @type Object
11415      */
11416     /**
11417      * @property  defaultHeaders
11418      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11419      * @type Object
11420      */
11421     /**
11422      * @property  method
11423      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11424      * @type String
11425      */
11426     /**
11427      * @property  timeout
11428      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11429      * @type Number
11430      */
11431
11432     /**
11433      * @property  autoAbort
11434      * Whether a new request should abort any pending requests. (defaults to false)
11435      * @type Boolean
11436      */
11437     autoAbort : false,
11438
11439     /**
11440      * Serialize the passed form into a url encoded string
11441      * @param {String/HTMLElement} form
11442      * @return {String}
11443      */
11444     serializeForm : function(form){
11445         return Roo.lib.Ajax.serializeForm(form);
11446     }
11447 });/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458  
11459 /**
11460  * @class Roo.UpdateManager
11461  * @extends Roo.util.Observable
11462  * Provides AJAX-style update for Element object.<br><br>
11463  * Usage:<br>
11464  * <pre><code>
11465  * // Get it from a Roo.Element object
11466  * var el = Roo.get("foo");
11467  * var mgr = el.getUpdateManager();
11468  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11469  * ...
11470  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11471  * <br>
11472  * // or directly (returns the same UpdateManager instance)
11473  * var mgr = new Roo.UpdateManager("myElementId");
11474  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11475  * mgr.on("update", myFcnNeedsToKnow);
11476  * <br>
11477    // short handed call directly from the element object
11478    Roo.get("foo").load({
11479         url: "bar.php",
11480         scripts:true,
11481         params: "for=bar",
11482         text: "Loading Foo..."
11483    });
11484  * </code></pre>
11485  * @constructor
11486  * Create new UpdateManager directly.
11487  * @param {String/HTMLElement/Roo.Element} el The element to update
11488  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11489  */
11490 Roo.UpdateManager = function(el, forceNew){
11491     el = Roo.get(el);
11492     if(!forceNew && el.updateManager){
11493         return el.updateManager;
11494     }
11495     /**
11496      * The Element object
11497      * @type Roo.Element
11498      */
11499     this.el = el;
11500     /**
11501      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11502      * @type String
11503      */
11504     this.defaultUrl = null;
11505
11506     this.addEvents({
11507         /**
11508          * @event beforeupdate
11509          * Fired before an update is made, return false from your handler and the update is cancelled.
11510          * @param {Roo.Element} el
11511          * @param {String/Object/Function} url
11512          * @param {String/Object} params
11513          */
11514         "beforeupdate": true,
11515         /**
11516          * @event update
11517          * Fired after successful update is made.
11518          * @param {Roo.Element} el
11519          * @param {Object} oResponseObject The response Object
11520          */
11521         "update": true,
11522         /**
11523          * @event failure
11524          * Fired on update failure.
11525          * @param {Roo.Element} el
11526          * @param {Object} oResponseObject The response Object
11527          */
11528         "failure": true
11529     });
11530     var d = Roo.UpdateManager.defaults;
11531     /**
11532      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11533      * @type String
11534      */
11535     this.sslBlankUrl = d.sslBlankUrl;
11536     /**
11537      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11538      * @type Boolean
11539      */
11540     this.disableCaching = d.disableCaching;
11541     /**
11542      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11543      * @type String
11544      */
11545     this.indicatorText = d.indicatorText;
11546     /**
11547      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11548      * @type String
11549      */
11550     this.showLoadIndicator = d.showLoadIndicator;
11551     /**
11552      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11553      * @type Number
11554      */
11555     this.timeout = d.timeout;
11556
11557     /**
11558      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11559      * @type Boolean
11560      */
11561     this.loadScripts = d.loadScripts;
11562
11563     /**
11564      * Transaction object of current executing transaction
11565      */
11566     this.transaction = null;
11567
11568     /**
11569      * @private
11570      */
11571     this.autoRefreshProcId = null;
11572     /**
11573      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11574      * @type Function
11575      */
11576     this.refreshDelegate = this.refresh.createDelegate(this);
11577     /**
11578      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11579      * @type Function
11580      */
11581     this.updateDelegate = this.update.createDelegate(this);
11582     /**
11583      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11584      * @type Function
11585      */
11586     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11587     /**
11588      * @private
11589      */
11590     this.successDelegate = this.processSuccess.createDelegate(this);
11591     /**
11592      * @private
11593      */
11594     this.failureDelegate = this.processFailure.createDelegate(this);
11595
11596     if(!this.renderer){
11597      /**
11598       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11599       */
11600     this.renderer = new Roo.UpdateManager.BasicRenderer();
11601     }
11602     
11603     Roo.UpdateManager.superclass.constructor.call(this);
11604 };
11605
11606 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11607     /**
11608      * Get the Element this UpdateManager is bound to
11609      * @return {Roo.Element} The element
11610      */
11611     getEl : function(){
11612         return this.el;
11613     },
11614     /**
11615      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11616      * @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:
11617 <pre><code>
11618 um.update({<br/>
11619     url: "your-url.php",<br/>
11620     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11621     callback: yourFunction,<br/>
11622     scope: yourObject, //(optional scope)  <br/>
11623     discardUrl: false, <br/>
11624     nocache: false,<br/>
11625     text: "Loading...",<br/>
11626     timeout: 30,<br/>
11627     scripts: false<br/>
11628 });
11629 </code></pre>
11630      * The only required property is url. The optional properties nocache, text and scripts
11631      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11632      * @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}
11633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11634      * @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.
11635      */
11636     update : function(url, params, callback, discardUrl){
11637         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11638             var method = this.method, cfg;
11639             if(typeof url == "object"){ // must be config object
11640                 cfg = url;
11641                 url = cfg.url;
11642                 params = params || cfg.params;
11643                 callback = callback || cfg.callback;
11644                 discardUrl = discardUrl || cfg.discardUrl;
11645                 if(callback && cfg.scope){
11646                     callback = callback.createDelegate(cfg.scope);
11647                 }
11648                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11649                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11650                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11651                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11652                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11653             }
11654             this.showLoading();
11655             if(!discardUrl){
11656                 this.defaultUrl = url;
11657             }
11658             if(typeof url == "function"){
11659                 url = url.call(this);
11660             }
11661
11662             method = method || (params ? "POST" : "GET");
11663             if(method == "GET"){
11664                 url = this.prepareUrl(url);
11665             }
11666
11667             var o = Roo.apply(cfg ||{}, {
11668                 url : url,
11669                 params: params,
11670                 success: this.successDelegate,
11671                 failure: this.failureDelegate,
11672                 callback: undefined,
11673                 timeout: (this.timeout*1000),
11674                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11675             });
11676
11677             this.transaction = Roo.Ajax.request(o);
11678         }
11679     },
11680
11681     /**
11682      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11683      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11684      * @param {String/HTMLElement} form The form Id or form element
11685      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11686      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11687      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11688      */
11689     formUpdate : function(form, url, reset, callback){
11690         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11691             if(typeof url == "function"){
11692                 url = url.call(this);
11693             }
11694             form = Roo.getDom(form);
11695             this.transaction = Roo.Ajax.request({
11696                 form: form,
11697                 url:url,
11698                 success: this.successDelegate,
11699                 failure: this.failureDelegate,
11700                 timeout: (this.timeout*1000),
11701                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11702             });
11703             this.showLoading.defer(1, this);
11704         }
11705     },
11706
11707     /**
11708      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11709      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11710      */
11711     refresh : function(callback){
11712         if(this.defaultUrl == null){
11713             return;
11714         }
11715         this.update(this.defaultUrl, null, callback, true);
11716     },
11717
11718     /**
11719      * Set this element to auto refresh.
11720      * @param {Number} interval How often to update (in seconds).
11721      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11722      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11723      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11724      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11725      */
11726     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11727         if(refreshNow){
11728             this.update(url || this.defaultUrl, params, callback, true);
11729         }
11730         if(this.autoRefreshProcId){
11731             clearInterval(this.autoRefreshProcId);
11732         }
11733         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11734     },
11735
11736     /**
11737      * Stop auto refresh on this element.
11738      */
11739      stopAutoRefresh : function(){
11740         if(this.autoRefreshProcId){
11741             clearInterval(this.autoRefreshProcId);
11742             delete this.autoRefreshProcId;
11743         }
11744     },
11745
11746     isAutoRefreshing : function(){
11747        return this.autoRefreshProcId ? true : false;
11748     },
11749     /**
11750      * Called to update the element to "Loading" state. Override to perform custom action.
11751      */
11752     showLoading : function(){
11753         if(this.showLoadIndicator){
11754             this.el.update(this.indicatorText);
11755         }
11756     },
11757
11758     /**
11759      * Adds unique parameter to query string if disableCaching = true
11760      * @private
11761      */
11762     prepareUrl : function(url){
11763         if(this.disableCaching){
11764             var append = "_dc=" + (new Date().getTime());
11765             if(url.indexOf("?") !== -1){
11766                 url += "&" + append;
11767             }else{
11768                 url += "?" + append;
11769             }
11770         }
11771         return url;
11772     },
11773
11774     /**
11775      * @private
11776      */
11777     processSuccess : function(response){
11778         this.transaction = null;
11779         if(response.argument.form && response.argument.reset){
11780             try{ // put in try/catch since some older FF releases had problems with this
11781                 response.argument.form.reset();
11782             }catch(e){}
11783         }
11784         if(this.loadScripts){
11785             this.renderer.render(this.el, response, this,
11786                 this.updateComplete.createDelegate(this, [response]));
11787         }else{
11788             this.renderer.render(this.el, response, this);
11789             this.updateComplete(response);
11790         }
11791     },
11792
11793     updateComplete : function(response){
11794         this.fireEvent("update", this.el, response);
11795         if(typeof response.argument.callback == "function"){
11796             response.argument.callback(this.el, true, response);
11797         }
11798     },
11799
11800     /**
11801      * @private
11802      */
11803     processFailure : function(response){
11804         this.transaction = null;
11805         this.fireEvent("failure", this.el, response);
11806         if(typeof response.argument.callback == "function"){
11807             response.argument.callback(this.el, false, response);
11808         }
11809     },
11810
11811     /**
11812      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11813      * @param {Object} renderer The object implementing the render() method
11814      */
11815     setRenderer : function(renderer){
11816         this.renderer = renderer;
11817     },
11818
11819     getRenderer : function(){
11820        return this.renderer;
11821     },
11822
11823     /**
11824      * Set the defaultUrl used for updates
11825      * @param {String/Function} defaultUrl The url or a function to call to get the url
11826      */
11827     setDefaultUrl : function(defaultUrl){
11828         this.defaultUrl = defaultUrl;
11829     },
11830
11831     /**
11832      * Aborts the executing transaction
11833      */
11834     abort : function(){
11835         if(this.transaction){
11836             Roo.Ajax.abort(this.transaction);
11837         }
11838     },
11839
11840     /**
11841      * Returns true if an update is in progress
11842      * @return {Boolean}
11843      */
11844     isUpdating : function(){
11845         if(this.transaction){
11846             return Roo.Ajax.isLoading(this.transaction);
11847         }
11848         return false;
11849     }
11850 });
11851
11852 /**
11853  * @class Roo.UpdateManager.defaults
11854  * @static (not really - but it helps the doc tool)
11855  * The defaults collection enables customizing the default properties of UpdateManager
11856  */
11857    Roo.UpdateManager.defaults = {
11858        /**
11859          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11860          * @type Number
11861          */
11862          timeout : 30,
11863
11864          /**
11865          * True to process scripts by default (Defaults to false).
11866          * @type Boolean
11867          */
11868         loadScripts : false,
11869
11870         /**
11871         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11872         * @type String
11873         */
11874         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11875         /**
11876          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11877          * @type Boolean
11878          */
11879         disableCaching : false,
11880         /**
11881          * Whether to show indicatorText when loading (Defaults to true).
11882          * @type Boolean
11883          */
11884         showLoadIndicator : true,
11885         /**
11886          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11887          * @type String
11888          */
11889         indicatorText : '<div class="loading-indicator">Loading...</div>'
11890    };
11891
11892 /**
11893  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11894  *Usage:
11895  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11896  * @param {String/HTMLElement/Roo.Element} el The element to update
11897  * @param {String} url The url
11898  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11899  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11900  * @static
11901  * @deprecated
11902  * @member Roo.UpdateManager
11903  */
11904 Roo.UpdateManager.updateElement = function(el, url, params, options){
11905     var um = Roo.get(el, true).getUpdateManager();
11906     Roo.apply(um, options);
11907     um.update(url, params, options ? options.callback : null);
11908 };
11909 // alias for backwards compat
11910 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11911 /**
11912  * @class Roo.UpdateManager.BasicRenderer
11913  * Default Content renderer. Updates the elements innerHTML with the responseText.
11914  */
11915 Roo.UpdateManager.BasicRenderer = function(){};
11916
11917 Roo.UpdateManager.BasicRenderer.prototype = {
11918     /**
11919      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11920      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11921      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11922      * @param {Roo.Element} el The element being rendered
11923      * @param {Object} response The YUI Connect response object
11924      * @param {UpdateManager} updateManager The calling update manager
11925      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11926      */
11927      render : function(el, response, updateManager, callback){
11928         el.update(response.responseText, updateManager.loadScripts, callback);
11929     }
11930 };
11931 /*
11932  * Based on:
11933  * Ext JS Library 1.1.1
11934  * Copyright(c) 2006-2007, Ext JS, LLC.
11935  *
11936  * Originally Released Under LGPL - original licence link has changed is not relivant.
11937  *
11938  * Fork - LGPL
11939  * <script type="text/javascript">
11940  */
11941
11942 /**
11943  * @class Roo.util.DelayedTask
11944  * Provides a convenient method of performing setTimeout where a new
11945  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11946  * You can use this class to buffer
11947  * the keypress events for a certain number of milliseconds, and perform only if they stop
11948  * for that amount of time.
11949  * @constructor The parameters to this constructor serve as defaults and are not required.
11950  * @param {Function} fn (optional) The default function to timeout
11951  * @param {Object} scope (optional) The default scope of that timeout
11952  * @param {Array} args (optional) The default Array of arguments
11953  */
11954 Roo.util.DelayedTask = function(fn, scope, args){
11955     var id = null, d, t;
11956
11957     var call = function(){
11958         var now = new Date().getTime();
11959         if(now - t >= d){
11960             clearInterval(id);
11961             id = null;
11962             fn.apply(scope, args || []);
11963         }
11964     };
11965     /**
11966      * Cancels any pending timeout and queues a new one
11967      * @param {Number} delay The milliseconds to delay
11968      * @param {Function} newFn (optional) Overrides function passed to constructor
11969      * @param {Object} newScope (optional) Overrides scope passed to constructor
11970      * @param {Array} newArgs (optional) Overrides args passed to constructor
11971      */
11972     this.delay = function(delay, newFn, newScope, newArgs){
11973         if(id && delay != d){
11974             this.cancel();
11975         }
11976         d = delay;
11977         t = new Date().getTime();
11978         fn = newFn || fn;
11979         scope = newScope || scope;
11980         args = newArgs || args;
11981         if(!id){
11982             id = setInterval(call, d);
11983         }
11984     };
11985
11986     /**
11987      * Cancel the last queued timeout
11988      */
11989     this.cancel = function(){
11990         if(id){
11991             clearInterval(id);
11992             id = null;
11993         }
11994     };
11995 };/*
11996  * Based on:
11997  * Ext JS Library 1.1.1
11998  * Copyright(c) 2006-2007, Ext JS, LLC.
11999  *
12000  * Originally Released Under LGPL - original licence link has changed is not relivant.
12001  *
12002  * Fork - LGPL
12003  * <script type="text/javascript">
12004  */
12005  
12006  
12007 Roo.util.TaskRunner = function(interval){
12008     interval = interval || 10;
12009     var tasks = [], removeQueue = [];
12010     var id = 0;
12011     var running = false;
12012
12013     var stopThread = function(){
12014         running = false;
12015         clearInterval(id);
12016         id = 0;
12017     };
12018
12019     var startThread = function(){
12020         if(!running){
12021             running = true;
12022             id = setInterval(runTasks, interval);
12023         }
12024     };
12025
12026     var removeTask = function(task){
12027         removeQueue.push(task);
12028         if(task.onStop){
12029             task.onStop();
12030         }
12031     };
12032
12033     var runTasks = function(){
12034         if(removeQueue.length > 0){
12035             for(var i = 0, len = removeQueue.length; i < len; i++){
12036                 tasks.remove(removeQueue[i]);
12037             }
12038             removeQueue = [];
12039             if(tasks.length < 1){
12040                 stopThread();
12041                 return;
12042             }
12043         }
12044         var now = new Date().getTime();
12045         for(var i = 0, len = tasks.length; i < len; ++i){
12046             var t = tasks[i];
12047             var itime = now - t.taskRunTime;
12048             if(t.interval <= itime){
12049                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12050                 t.taskRunTime = now;
12051                 if(rt === false || t.taskRunCount === t.repeat){
12052                     removeTask(t);
12053                     return;
12054                 }
12055             }
12056             if(t.duration && t.duration <= (now - t.taskStartTime)){
12057                 removeTask(t);
12058             }
12059         }
12060     };
12061
12062     /**
12063      * Queues a new task.
12064      * @param {Object} task
12065      */
12066     this.start = function(task){
12067         tasks.push(task);
12068         task.taskStartTime = new Date().getTime();
12069         task.taskRunTime = 0;
12070         task.taskRunCount = 0;
12071         startThread();
12072         return task;
12073     };
12074
12075     this.stop = function(task){
12076         removeTask(task);
12077         return task;
12078     };
12079
12080     this.stopAll = function(){
12081         stopThread();
12082         for(var i = 0, len = tasks.length; i < len; i++){
12083             if(tasks[i].onStop){
12084                 tasks[i].onStop();
12085             }
12086         }
12087         tasks = [];
12088         removeQueue = [];
12089     };
12090 };
12091
12092 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103  
12104 /**
12105  * @class Roo.util.MixedCollection
12106  * @extends Roo.util.Observable
12107  * A Collection class that maintains both numeric indexes and keys and exposes events.
12108  * @constructor
12109  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12110  * collection (defaults to false)
12111  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12112  * and return the key value for that item.  This is used when available to look up the key on items that
12113  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12114  * equivalent to providing an implementation for the {@link #getKey} method.
12115  */
12116 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12117     this.items = [];
12118     this.map = {};
12119     this.keys = [];
12120     this.length = 0;
12121     this.addEvents({
12122         /**
12123          * @event clear
12124          * Fires when the collection is cleared.
12125          */
12126         "clear" : true,
12127         /**
12128          * @event add
12129          * Fires when an item is added to the collection.
12130          * @param {Number} index The index at which the item was added.
12131          * @param {Object} o The item added.
12132          * @param {String} key The key associated with the added item.
12133          */
12134         "add" : true,
12135         /**
12136          * @event replace
12137          * Fires when an item is replaced in the collection.
12138          * @param {String} key he key associated with the new added.
12139          * @param {Object} old The item being replaced.
12140          * @param {Object} new The new item.
12141          */
12142         "replace" : true,
12143         /**
12144          * @event remove
12145          * Fires when an item is removed from the collection.
12146          * @param {Object} o The item being removed.
12147          * @param {String} key (optional) The key associated with the removed item.
12148          */
12149         "remove" : true,
12150         "sort" : true
12151     });
12152     this.allowFunctions = allowFunctions === true;
12153     if(keyFn){
12154         this.getKey = keyFn;
12155     }
12156     Roo.util.MixedCollection.superclass.constructor.call(this);
12157 };
12158
12159 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12160     allowFunctions : false,
12161     
12162 /**
12163  * Adds an item to the collection.
12164  * @param {String} key The key to associate with the item
12165  * @param {Object} o The item to add.
12166  * @return {Object} The item added.
12167  */
12168     add : function(key, o){
12169         if(arguments.length == 1){
12170             o = arguments[0];
12171             key = this.getKey(o);
12172         }
12173         if(typeof key == "undefined" || key === null){
12174             this.length++;
12175             this.items.push(o);
12176             this.keys.push(null);
12177         }else{
12178             var old = this.map[key];
12179             if(old){
12180                 return this.replace(key, o);
12181             }
12182             this.length++;
12183             this.items.push(o);
12184             this.map[key] = o;
12185             this.keys.push(key);
12186         }
12187         this.fireEvent("add", this.length-1, o, key);
12188         return o;
12189     },
12190    
12191 /**
12192   * MixedCollection has a generic way to fetch keys if you implement getKey.
12193 <pre><code>
12194 // normal way
12195 var mc = new Roo.util.MixedCollection();
12196 mc.add(someEl.dom.id, someEl);
12197 mc.add(otherEl.dom.id, otherEl);
12198 //and so on
12199
12200 // using getKey
12201 var mc = new Roo.util.MixedCollection();
12202 mc.getKey = function(el){
12203    return el.dom.id;
12204 };
12205 mc.add(someEl);
12206 mc.add(otherEl);
12207
12208 // or via the constructor
12209 var mc = new Roo.util.MixedCollection(false, function(el){
12210    return el.dom.id;
12211 });
12212 mc.add(someEl);
12213 mc.add(otherEl);
12214 </code></pre>
12215  * @param o {Object} The item for which to find the key.
12216  * @return {Object} The key for the passed item.
12217  */
12218     getKey : function(o){
12219          return o.id; 
12220     },
12221    
12222 /**
12223  * Replaces an item in the collection.
12224  * @param {String} key The key associated with the item to replace, or the item to replace.
12225  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12226  * @return {Object}  The new item.
12227  */
12228     replace : function(key, o){
12229         if(arguments.length == 1){
12230             o = arguments[0];
12231             key = this.getKey(o);
12232         }
12233         var old = this.item(key);
12234         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12235              return this.add(key, o);
12236         }
12237         var index = this.indexOfKey(key);
12238         this.items[index] = o;
12239         this.map[key] = o;
12240         this.fireEvent("replace", key, old, o);
12241         return o;
12242     },
12243    
12244 /**
12245  * Adds all elements of an Array or an Object to the collection.
12246  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12247  * an Array of values, each of which are added to the collection.
12248  */
12249     addAll : function(objs){
12250         if(arguments.length > 1 || objs instanceof Array){
12251             var args = arguments.length > 1 ? arguments : objs;
12252             for(var i = 0, len = args.length; i < len; i++){
12253                 this.add(args[i]);
12254             }
12255         }else{
12256             for(var key in objs){
12257                 if(this.allowFunctions || typeof objs[key] != "function"){
12258                     this.add(key, objs[key]);
12259                 }
12260             }
12261         }
12262     },
12263    
12264 /**
12265  * Executes the specified function once for every item in the collection, passing each
12266  * item as the first and only parameter. returning false from the function will stop the iteration.
12267  * @param {Function} fn The function to execute for each item.
12268  * @param {Object} scope (optional) The scope in which to execute the function.
12269  */
12270     each : function(fn, scope){
12271         var items = [].concat(this.items); // each safe for removal
12272         for(var i = 0, len = items.length; i < len; i++){
12273             if(fn.call(scope || items[i], items[i], i, len) === false){
12274                 break;
12275             }
12276         }
12277     },
12278    
12279 /**
12280  * Executes the specified function once for every key in the collection, passing each
12281  * key, and its associated item as the first two parameters.
12282  * @param {Function} fn The function to execute for each item.
12283  * @param {Object} scope (optional) The scope in which to execute the function.
12284  */
12285     eachKey : function(fn, scope){
12286         for(var i = 0, len = this.keys.length; i < len; i++){
12287             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12288         }
12289     },
12290    
12291 /**
12292  * Returns the first item in the collection which elicits a true return value from the
12293  * passed selection function.
12294  * @param {Function} fn The selection function to execute for each item.
12295  * @param {Object} scope (optional) The scope in which to execute the function.
12296  * @return {Object} The first item in the collection which returned true from the selection function.
12297  */
12298     find : function(fn, scope){
12299         for(var i = 0, len = this.items.length; i < len; i++){
12300             if(fn.call(scope || window, this.items[i], this.keys[i])){
12301                 return this.items[i];
12302             }
12303         }
12304         return null;
12305     },
12306    
12307 /**
12308  * Inserts an item at the specified index in the collection.
12309  * @param {Number} index The index to insert the item at.
12310  * @param {String} key The key to associate with the new item, or the item itself.
12311  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12312  * @return {Object} The item inserted.
12313  */
12314     insert : function(index, key, o){
12315         if(arguments.length == 2){
12316             o = arguments[1];
12317             key = this.getKey(o);
12318         }
12319         if(index >= this.length){
12320             return this.add(key, o);
12321         }
12322         this.length++;
12323         this.items.splice(index, 0, o);
12324         if(typeof key != "undefined" && key != null){
12325             this.map[key] = o;
12326         }
12327         this.keys.splice(index, 0, key);
12328         this.fireEvent("add", index, o, key);
12329         return o;
12330     },
12331    
12332 /**
12333  * Removed an item from the collection.
12334  * @param {Object} o The item to remove.
12335  * @return {Object} The item removed.
12336  */
12337     remove : function(o){
12338         return this.removeAt(this.indexOf(o));
12339     },
12340    
12341 /**
12342  * Remove an item from a specified index in the collection.
12343  * @param {Number} index The index within the collection of the item to remove.
12344  */
12345     removeAt : function(index){
12346         if(index < this.length && index >= 0){
12347             this.length--;
12348             var o = this.items[index];
12349             this.items.splice(index, 1);
12350             var key = this.keys[index];
12351             if(typeof key != "undefined"){
12352                 delete this.map[key];
12353             }
12354             this.keys.splice(index, 1);
12355             this.fireEvent("remove", o, key);
12356         }
12357     },
12358    
12359 /**
12360  * Removed an item associated with the passed key fom the collection.
12361  * @param {String} key The key of the item to remove.
12362  */
12363     removeKey : function(key){
12364         return this.removeAt(this.indexOfKey(key));
12365     },
12366    
12367 /**
12368  * Returns the number of items in the collection.
12369  * @return {Number} the number of items in the collection.
12370  */
12371     getCount : function(){
12372         return this.length; 
12373     },
12374    
12375 /**
12376  * Returns index within the collection of the passed Object.
12377  * @param {Object} o The item to find the index of.
12378  * @return {Number} index of the item.
12379  */
12380     indexOf : function(o){
12381         if(!this.items.indexOf){
12382             for(var i = 0, len = this.items.length; i < len; i++){
12383                 if(this.items[i] == o) return i;
12384             }
12385             return -1;
12386         }else{
12387             return this.items.indexOf(o);
12388         }
12389     },
12390    
12391 /**
12392  * Returns index within the collection of the passed key.
12393  * @param {String} key The key to find the index of.
12394  * @return {Number} index of the key.
12395  */
12396     indexOfKey : function(key){
12397         if(!this.keys.indexOf){
12398             for(var i = 0, len = this.keys.length; i < len; i++){
12399                 if(this.keys[i] == key) return i;
12400             }
12401             return -1;
12402         }else{
12403             return this.keys.indexOf(key);
12404         }
12405     },
12406    
12407 /**
12408  * Returns the item associated with the passed key OR index. Key has priority over index.
12409  * @param {String/Number} key The key or index of the item.
12410  * @return {Object} The item associated with the passed key.
12411  */
12412     item : function(key){
12413         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12414         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12415     },
12416     
12417 /**
12418  * Returns the item at the specified index.
12419  * @param {Number} index The index of the item.
12420  * @return {Object}
12421  */
12422     itemAt : function(index){
12423         return this.items[index];
12424     },
12425     
12426 /**
12427  * Returns the item associated with the passed key.
12428  * @param {String/Number} key The key of the item.
12429  * @return {Object} The item associated with the passed key.
12430  */
12431     key : function(key){
12432         return this.map[key];
12433     },
12434    
12435 /**
12436  * Returns true if the collection contains the passed Object as an item.
12437  * @param {Object} o  The Object to look for in the collection.
12438  * @return {Boolean} True if the collection contains the Object as an item.
12439  */
12440     contains : function(o){
12441         return this.indexOf(o) != -1;
12442     },
12443    
12444 /**
12445  * Returns true if the collection contains the passed Object as a key.
12446  * @param {String} key The key to look for in the collection.
12447  * @return {Boolean} True if the collection contains the Object as a key.
12448  */
12449     containsKey : function(key){
12450         return typeof this.map[key] != "undefined";
12451     },
12452    
12453 /**
12454  * Removes all items from the collection.
12455  */
12456     clear : function(){
12457         this.length = 0;
12458         this.items = [];
12459         this.keys = [];
12460         this.map = {};
12461         this.fireEvent("clear");
12462     },
12463    
12464 /**
12465  * Returns the first item in the collection.
12466  * @return {Object} the first item in the collection..
12467  */
12468     first : function(){
12469         return this.items[0]; 
12470     },
12471    
12472 /**
12473  * Returns the last item in the collection.
12474  * @return {Object} the last item in the collection..
12475  */
12476     last : function(){
12477         return this.items[this.length-1];   
12478     },
12479     
12480     _sort : function(property, dir, fn){
12481         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12482         fn = fn || function(a, b){
12483             return a-b;
12484         };
12485         var c = [], k = this.keys, items = this.items;
12486         for(var i = 0, len = items.length; i < len; i++){
12487             c[c.length] = {key: k[i], value: items[i], index: i};
12488         }
12489         c.sort(function(a, b){
12490             var v = fn(a[property], b[property]) * dsc;
12491             if(v == 0){
12492                 v = (a.index < b.index ? -1 : 1);
12493             }
12494             return v;
12495         });
12496         for(var i = 0, len = c.length; i < len; i++){
12497             items[i] = c[i].value;
12498             k[i] = c[i].key;
12499         }
12500         this.fireEvent("sort", this);
12501     },
12502     
12503     /**
12504      * Sorts this collection with the passed comparison function
12505      * @param {String} direction (optional) "ASC" or "DESC"
12506      * @param {Function} fn (optional) comparison function
12507      */
12508     sort : function(dir, fn){
12509         this._sort("value", dir, fn);
12510     },
12511     
12512     /**
12513      * Sorts this collection by keys
12514      * @param {String} direction (optional) "ASC" or "DESC"
12515      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12516      */
12517     keySort : function(dir, fn){
12518         this._sort("key", dir, fn || function(a, b){
12519             return String(a).toUpperCase()-String(b).toUpperCase();
12520         });
12521     },
12522     
12523     /**
12524      * Returns a range of items in this collection
12525      * @param {Number} startIndex (optional) defaults to 0
12526      * @param {Number} endIndex (optional) default to the last item
12527      * @return {Array} An array of items
12528      */
12529     getRange : function(start, end){
12530         var items = this.items;
12531         if(items.length < 1){
12532             return [];
12533         }
12534         start = start || 0;
12535         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12536         var r = [];
12537         if(start <= end){
12538             for(var i = start; i <= end; i++) {
12539                     r[r.length] = items[i];
12540             }
12541         }else{
12542             for(var i = start; i >= end; i--) {
12543                     r[r.length] = items[i];
12544             }
12545         }
12546         return r;
12547     },
12548         
12549     /**
12550      * Filter the <i>objects</i> in this collection by a specific property. 
12551      * Returns a new collection that has been filtered.
12552      * @param {String} property A property on your objects
12553      * @param {String/RegExp} value Either string that the property values 
12554      * should start with or a RegExp to test against the property
12555      * @return {MixedCollection} The new filtered collection
12556      */
12557     filter : function(property, value){
12558         if(!value.exec){ // not a regex
12559             value = String(value);
12560             if(value.length == 0){
12561                 return this.clone();
12562             }
12563             value = new RegExp("^" + Roo.escapeRe(value), "i");
12564         }
12565         return this.filterBy(function(o){
12566             return o && value.test(o[property]);
12567         });
12568         },
12569     
12570     /**
12571      * Filter by a function. * Returns a new collection that has been filtered.
12572      * The passed function will be called with each 
12573      * object in the collection. If the function returns true, the value is included 
12574      * otherwise it is filtered.
12575      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12576      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12577      * @return {MixedCollection} The new filtered collection
12578      */
12579     filterBy : function(fn, scope){
12580         var r = new Roo.util.MixedCollection();
12581         r.getKey = this.getKey;
12582         var k = this.keys, it = this.items;
12583         for(var i = 0, len = it.length; i < len; i++){
12584             if(fn.call(scope||this, it[i], k[i])){
12585                                 r.add(k[i], it[i]);
12586                         }
12587         }
12588         return r;
12589     },
12590     
12591     /**
12592      * Creates a duplicate of this collection
12593      * @return {MixedCollection}
12594      */
12595     clone : function(){
12596         var r = new Roo.util.MixedCollection();
12597         var k = this.keys, it = this.items;
12598         for(var i = 0, len = it.length; i < len; i++){
12599             r.add(k[i], it[i]);
12600         }
12601         r.getKey = this.getKey;
12602         return r;
12603     }
12604 });
12605 /**
12606  * Returns the item associated with the passed key or index.
12607  * @method
12608  * @param {String/Number} key The key or index of the item.
12609  * @return {Object} The item associated with the passed key.
12610  */
12611 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12612  * Based on:
12613  * Ext JS Library 1.1.1
12614  * Copyright(c) 2006-2007, Ext JS, LLC.
12615  *
12616  * Originally Released Under LGPL - original licence link has changed is not relivant.
12617  *
12618  * Fork - LGPL
12619  * <script type="text/javascript">
12620  */
12621 /**
12622  * @class Roo.util.JSON
12623  * Modified version of Douglas Crockford"s json.js that doesn"t
12624  * mess with the Object prototype 
12625  * http://www.json.org/js.html
12626  * @singleton
12627  */
12628 Roo.util.JSON = new (function(){
12629     var useHasOwn = {}.hasOwnProperty ? true : false;
12630     
12631     // crashes Safari in some instances
12632     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12633     
12634     var pad = function(n) {
12635         return n < 10 ? "0" + n : n;
12636     };
12637     
12638     var m = {
12639         "\b": '\\b',
12640         "\t": '\\t',
12641         "\n": '\\n',
12642         "\f": '\\f',
12643         "\r": '\\r',
12644         '"' : '\\"',
12645         "\\": '\\\\'
12646     };
12647
12648     var encodeString = function(s){
12649         if (/["\\\x00-\x1f]/.test(s)) {
12650             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12651                 var c = m[b];
12652                 if(c){
12653                     return c;
12654                 }
12655                 c = b.charCodeAt();
12656                 return "\\u00" +
12657                     Math.floor(c / 16).toString(16) +
12658                     (c % 16).toString(16);
12659             }) + '"';
12660         }
12661         return '"' + s + '"';
12662     };
12663     
12664     var encodeArray = function(o){
12665         var a = ["["], b, i, l = o.length, v;
12666             for (i = 0; i < l; i += 1) {
12667                 v = o[i];
12668                 switch (typeof v) {
12669                     case "undefined":
12670                     case "function":
12671                     case "unknown":
12672                         break;
12673                     default:
12674                         if (b) {
12675                             a.push(',');
12676                         }
12677                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12678                         b = true;
12679                 }
12680             }
12681             a.push("]");
12682             return a.join("");
12683     };
12684     
12685     var encodeDate = function(o){
12686         return '"' + o.getFullYear() + "-" +
12687                 pad(o.getMonth() + 1) + "-" +
12688                 pad(o.getDate()) + "T" +
12689                 pad(o.getHours()) + ":" +
12690                 pad(o.getMinutes()) + ":" +
12691                 pad(o.getSeconds()) + '"';
12692     };
12693     
12694     /**
12695      * Encodes an Object, Array or other value
12696      * @param {Mixed} o The variable to encode
12697      * @return {String} The JSON string
12698      */
12699     this.encode = function(o){
12700         if(typeof o == "undefined" || o === null){
12701             return "null";
12702         }else if(o instanceof Array){
12703             return encodeArray(o);
12704         }else if(o instanceof Date){
12705             return encodeDate(o);
12706         }else if(typeof o == "string"){
12707             return encodeString(o);
12708         }else if(typeof o == "number"){
12709             return isFinite(o) ? String(o) : "null";
12710         }else if(typeof o == "boolean"){
12711             return String(o);
12712         }else {
12713             var a = ["{"], b, i, v;
12714             for (i in o) {
12715                 if(!useHasOwn || o.hasOwnProperty(i)) {
12716                     v = o[i];
12717                     switch (typeof v) {
12718                     case "undefined":
12719                     case "function":
12720                     case "unknown":
12721                         break;
12722                     default:
12723                         if(b){
12724                             a.push(',');
12725                         }
12726                         a.push(this.encode(i), ":",
12727                                 v === null ? "null" : this.encode(v));
12728                         b = true;
12729                     }
12730                 }
12731             }
12732             a.push("}");
12733             return a.join("");
12734         }
12735     };
12736     
12737     /**
12738      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12739      * @param {String} json The JSON string
12740      * @return {Object} The resulting object
12741      */
12742     this.decode = function(json){
12743         /**
12744          * eval:var:json
12745          */
12746         return eval("(" + json + ')');
12747     };
12748 })();
12749 /** 
12750  * Shorthand for {@link Roo.util.JSON#encode}
12751  * @member Roo encode 
12752  * @method */
12753 Roo.encode = Roo.util.JSON.encode;
12754 /** 
12755  * Shorthand for {@link Roo.util.JSON#decode}
12756  * @member Roo decode 
12757  * @method */
12758 Roo.decode = Roo.util.JSON.decode;
12759 /*
12760  * Based on:
12761  * Ext JS Library 1.1.1
12762  * Copyright(c) 2006-2007, Ext JS, LLC.
12763  *
12764  * Originally Released Under LGPL - original licence link has changed is not relivant.
12765  *
12766  * Fork - LGPL
12767  * <script type="text/javascript">
12768  */
12769  
12770 /**
12771  * @class Roo.util.Format
12772  * Reusable data formatting functions
12773  * @singleton
12774  */
12775 Roo.util.Format = function(){
12776     var trimRe = /^\s+|\s+$/g;
12777     return {
12778         /**
12779          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12780          * @param {String} value The string to truncate
12781          * @param {Number} length The maximum length to allow before truncating
12782          * @return {String} The converted text
12783          */
12784         ellipsis : function(value, len){
12785             if(value && value.length > len){
12786                 return value.substr(0, len-3)+"...";
12787             }
12788             return value;
12789         },
12790
12791         /**
12792          * Checks a reference and converts it to empty string if it is undefined
12793          * @param {Mixed} value Reference to check
12794          * @return {Mixed} Empty string if converted, otherwise the original value
12795          */
12796         undef : function(value){
12797             return typeof value != "undefined" ? value : "";
12798         },
12799
12800         /**
12801          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12802          * @param {String} value The string to encode
12803          * @return {String} The encoded text
12804          */
12805         htmlEncode : function(value){
12806             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12807         },
12808
12809         /**
12810          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12811          * @param {String} value The string to decode
12812          * @return {String} The decoded text
12813          */
12814         htmlDecode : function(value){
12815             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12816         },
12817
12818         /**
12819          * Trims any whitespace from either side of a string
12820          * @param {String} value The text to trim
12821          * @return {String} The trimmed text
12822          */
12823         trim : function(value){
12824             return String(value).replace(trimRe, "");
12825         },
12826
12827         /**
12828          * Returns a substring from within an original string
12829          * @param {String} value The original text
12830          * @param {Number} start The start index of the substring
12831          * @param {Number} length The length of the substring
12832          * @return {String} The substring
12833          */
12834         substr : function(value, start, length){
12835             return String(value).substr(start, length);
12836         },
12837
12838         /**
12839          * Converts a string to all lower case letters
12840          * @param {String} value The text to convert
12841          * @return {String} The converted text
12842          */
12843         lowercase : function(value){
12844             return String(value).toLowerCase();
12845         },
12846
12847         /**
12848          * Converts a string to all upper case letters
12849          * @param {String} value The text to convert
12850          * @return {String} The converted text
12851          */
12852         uppercase : function(value){
12853             return String(value).toUpperCase();
12854         },
12855
12856         /**
12857          * Converts the first character only of a string to upper case
12858          * @param {String} value The text to convert
12859          * @return {String} The converted text
12860          */
12861         capitalize : function(value){
12862             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12863         },
12864
12865         // private
12866         call : function(value, fn){
12867             if(arguments.length > 2){
12868                 var args = Array.prototype.slice.call(arguments, 2);
12869                 args.unshift(value);
12870                  
12871                 return /** eval:var:value */  eval(fn).apply(window, args);
12872             }else{
12873                 /** eval:var:value */
12874                 return /** eval:var:value */ eval(fn).call(window, value);
12875             }
12876         },
12877
12878         /**
12879          * Format a number as US currency
12880          * @param {Number/String} value The numeric value to format
12881          * @return {String} The formatted currency string
12882          */
12883         usMoney : function(v){
12884             v = (Math.round((v-0)*100))/100;
12885             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12886             v = String(v);
12887             var ps = v.split('.');
12888             var whole = ps[0];
12889             var sub = ps[1] ? '.'+ ps[1] : '.00';
12890             var r = /(\d+)(\d{3})/;
12891             while (r.test(whole)) {
12892                 whole = whole.replace(r, '$1' + ',' + '$2');
12893             }
12894             return "$" + whole + sub ;
12895         },
12896
12897         /**
12898          * Parse a value into a formatted date using the specified format pattern.
12899          * @param {Mixed} value The value to format
12900          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12901          * @return {String} The formatted date string
12902          */
12903         date : function(v, format){
12904             if(!v){
12905                 return "";
12906             }
12907             if(!(v instanceof Date)){
12908                 v = new Date(Date.parse(v));
12909             }
12910             return v.dateFormat(format || "m/d/Y");
12911         },
12912
12913         /**
12914          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12915          * @param {String} format Any valid date format string
12916          * @return {Function} The date formatting function
12917          */
12918         dateRenderer : function(format){
12919             return function(v){
12920                 return Roo.util.Format.date(v, format);  
12921             };
12922         },
12923
12924         // private
12925         stripTagsRE : /<\/?[^>]+>/gi,
12926         
12927         /**
12928          * Strips all HTML tags
12929          * @param {Mixed} value The text from which to strip tags
12930          * @return {String} The stripped text
12931          */
12932         stripTags : function(v){
12933             return !v ? v : String(v).replace(this.stripTagsRE, "");
12934         }
12935     };
12936 }();/*
12937  * Based on:
12938  * Ext JS Library 1.1.1
12939  * Copyright(c) 2006-2007, Ext JS, LLC.
12940  *
12941  * Originally Released Under LGPL - original licence link has changed is not relivant.
12942  *
12943  * Fork - LGPL
12944  * <script type="text/javascript">
12945  */
12946
12947
12948  
12949
12950 /**
12951  * @class Roo.MasterTemplate
12952  * @extends Roo.Template
12953  * Provides a template that can have child templates. The syntax is:
12954 <pre><code>
12955 var t = new Roo.MasterTemplate(
12956         '&lt;select name="{name}"&gt;',
12957                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12958         '&lt;/select&gt;'
12959 );
12960 t.add('options', {value: 'foo', text: 'bar'});
12961 // or you can add multiple child elements in one shot
12962 t.addAll('options', [
12963     {value: 'foo', text: 'bar'},
12964     {value: 'foo2', text: 'bar2'},
12965     {value: 'foo3', text: 'bar3'}
12966 ]);
12967 // then append, applying the master template values
12968 t.append('my-form', {name: 'my-select'});
12969 </code></pre>
12970 * A name attribute for the child template is not required if you have only one child
12971 * template or you want to refer to them by index.
12972  */
12973 Roo.MasterTemplate = function(){
12974     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
12975     this.originalHtml = this.html;
12976     var st = {};
12977     var m, re = this.subTemplateRe;
12978     re.lastIndex = 0;
12979     var subIndex = 0;
12980     while(m = re.exec(this.html)){
12981         var name = m[1], content = m[2];
12982         st[subIndex] = {
12983             name: name,
12984             index: subIndex,
12985             buffer: [],
12986             tpl : new Roo.Template(content)
12987         };
12988         if(name){
12989             st[name] = st[subIndex];
12990         }
12991         st[subIndex].tpl.compile();
12992         st[subIndex].tpl.call = this.call.createDelegate(this);
12993         subIndex++;
12994     }
12995     this.subCount = subIndex;
12996     this.subs = st;
12997 };
12998 Roo.extend(Roo.MasterTemplate, Roo.Template, {
12999     /**
13000     * The regular expression used to match sub templates
13001     * @type RegExp
13002     * @property
13003     */
13004     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13005
13006     /**
13007      * Applies the passed values to a child template.
13008      * @param {String/Number} name (optional) The name or index of the child template
13009      * @param {Array/Object} values The values to be applied to the template
13010      * @return {MasterTemplate} this
13011      */
13012      add : function(name, values){
13013         if(arguments.length == 1){
13014             values = arguments[0];
13015             name = 0;
13016         }
13017         var s = this.subs[name];
13018         s.buffer[s.buffer.length] = s.tpl.apply(values);
13019         return this;
13020     },
13021
13022     /**
13023      * Applies all the passed values to a child template.
13024      * @param {String/Number} name (optional) The name or index of the child template
13025      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13026      * @param {Boolean} reset (optional) True to reset the template first
13027      * @return {MasterTemplate} this
13028      */
13029     fill : function(name, values, reset){
13030         var a = arguments;
13031         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13032             values = a[0];
13033             name = 0;
13034             reset = a[1];
13035         }
13036         if(reset){
13037             this.reset();
13038         }
13039         for(var i = 0, len = values.length; i < len; i++){
13040             this.add(name, values[i]);
13041         }
13042         return this;
13043     },
13044
13045     /**
13046      * Resets the template for reuse
13047      * @return {MasterTemplate} this
13048      */
13049      reset : function(){
13050         var s = this.subs;
13051         for(var i = 0; i < this.subCount; i++){
13052             s[i].buffer = [];
13053         }
13054         return this;
13055     },
13056
13057     applyTemplate : function(values){
13058         var s = this.subs;
13059         var replaceIndex = -1;
13060         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13061             return s[++replaceIndex].buffer.join("");
13062         });
13063         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13064     },
13065
13066     apply : function(){
13067         return this.applyTemplate.apply(this, arguments);
13068     },
13069
13070     compile : function(){return this;}
13071 });
13072
13073 /**
13074  * Alias for fill().
13075  * @method
13076  */
13077 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13078  /**
13079  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13080  * var tpl = Roo.MasterTemplate.from('element-id');
13081  * @param {String/HTMLElement} el
13082  * @param {Object} config
13083  * @static
13084  */
13085 Roo.MasterTemplate.from = function(el, config){
13086     el = Roo.getDom(el);
13087     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13088 };/*
13089  * Based on:
13090  * Ext JS Library 1.1.1
13091  * Copyright(c) 2006-2007, Ext JS, LLC.
13092  *
13093  * Originally Released Under LGPL - original licence link has changed is not relivant.
13094  *
13095  * Fork - LGPL
13096  * <script type="text/javascript">
13097  */
13098
13099  
13100 /**
13101  * @class Roo.util.CSS
13102  * Utility class for manipulating CSS rules
13103  * @singleton
13104  */
13105 Roo.util.CSS = function(){
13106         var rules = null;
13107         var doc = document;
13108
13109     var camelRe = /(-[a-z])/gi;
13110     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13111
13112    return {
13113    /**
13114     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13115     * tag and appended to the HEAD of the document.
13116     * @param {String} cssText The text containing the css rules
13117     * @param {String} id An id to add to the stylesheet for later removal
13118     * @return {StyleSheet}
13119     */
13120    createStyleSheet : function(cssText, id){
13121        var ss;
13122        var head = doc.getElementsByTagName("head")[0];
13123        var rules = doc.createElement("style");
13124        rules.setAttribute("type", "text/css");
13125        if(id){
13126            rules.setAttribute("id", id);
13127        }
13128        if(Roo.isIE){
13129            head.appendChild(rules);
13130            ss = rules.styleSheet;
13131            ss.cssText = cssText;
13132        }else{
13133            try{
13134                 rules.appendChild(doc.createTextNode(cssText));
13135            }catch(e){
13136                rules.cssText = cssText; 
13137            }
13138            head.appendChild(rules);
13139            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13140        }
13141        this.cacheStyleSheet(ss);
13142        return ss;
13143    },
13144
13145    /**
13146     * Removes a style or link tag by id
13147     * @param {String} id The id of the tag
13148     */
13149    removeStyleSheet : function(id){
13150        var existing = doc.getElementById(id);
13151        if(existing){
13152            existing.parentNode.removeChild(existing);
13153        }
13154    },
13155
13156    /**
13157     * Dynamically swaps an existing stylesheet reference for a new one
13158     * @param {String} id The id of an existing link tag to remove
13159     * @param {String} url The href of the new stylesheet to include
13160     */
13161    swapStyleSheet : function(id, url){
13162        this.removeStyleSheet(id);
13163        var ss = doc.createElement("link");
13164        ss.setAttribute("rel", "stylesheet");
13165        ss.setAttribute("type", "text/css");
13166        ss.setAttribute("id", id);
13167        ss.setAttribute("href", url);
13168        doc.getElementsByTagName("head")[0].appendChild(ss);
13169    },
13170    
13171    /**
13172     * Refresh the rule cache if you have dynamically added stylesheets
13173     * @return {Object} An object (hash) of rules indexed by selector
13174     */
13175    refreshCache : function(){
13176        return this.getRules(true);
13177    },
13178
13179    // private
13180    cacheStyleSheet : function(ss){
13181        if(!rules){
13182            rules = {};
13183        }
13184        try{// try catch for cross domain access issue
13185            var ssRules = ss.cssRules || ss.rules;
13186            for(var j = ssRules.length-1; j >= 0; --j){
13187                rules[ssRules[j].selectorText] = ssRules[j];
13188            }
13189        }catch(e){}
13190    },
13191    
13192    /**
13193     * Gets all css rules for the document
13194     * @param {Boolean} refreshCache true to refresh the internal cache
13195     * @return {Object} An object (hash) of rules indexed by selector
13196     */
13197    getRules : function(refreshCache){
13198                 if(rules == null || refreshCache){
13199                         rules = {};
13200                         var ds = doc.styleSheets;
13201                         for(var i =0, len = ds.length; i < len; i++){
13202                             try{
13203                         this.cacheStyleSheet(ds[i]);
13204                     }catch(e){} 
13205                 }
13206                 }
13207                 return rules;
13208         },
13209         
13210         /**
13211     * Gets an an individual CSS rule by selector(s)
13212     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13213     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13214     * @return {CSSRule} The CSS rule or null if one is not found
13215     */
13216    getRule : function(selector, refreshCache){
13217                 var rs = this.getRules(refreshCache);
13218                 if(!(selector instanceof Array)){
13219                     return rs[selector];
13220                 }
13221                 for(var i = 0; i < selector.length; i++){
13222                         if(rs[selector[i]]){
13223                                 return rs[selector[i]];
13224                         }
13225                 }
13226                 return null;
13227         },
13228         
13229         
13230         /**
13231     * Updates a rule property
13232     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13233     * @param {String} property The css property
13234     * @param {String} value The new value for the property
13235     * @return {Boolean} true If a rule was found and updated
13236     */
13237    updateRule : function(selector, property, value){
13238                 if(!(selector instanceof Array)){
13239                         var rule = this.getRule(selector);
13240                         if(rule){
13241                                 rule.style[property.replace(camelRe, camelFn)] = value;
13242                                 return true;
13243                         }
13244                 }else{
13245                         for(var i = 0; i < selector.length; i++){
13246                                 if(this.updateRule(selector[i], property, value)){
13247                                         return true;
13248                                 }
13249                         }
13250                 }
13251                 return false;
13252         }
13253    };   
13254 }();/*
13255  * Based on:
13256  * Ext JS Library 1.1.1
13257  * Copyright(c) 2006-2007, Ext JS, LLC.
13258  *
13259  * Originally Released Under LGPL - original licence link has changed is not relivant.
13260  *
13261  * Fork - LGPL
13262  * <script type="text/javascript">
13263  */
13264
13265  
13266
13267 /**
13268  * @class Roo.util.ClickRepeater
13269  * @extends Roo.util.Observable
13270  * 
13271  * A wrapper class which can be applied to any element. Fires a "click" event while the
13272  * mouse is pressed. The interval between firings may be specified in the config but
13273  * defaults to 10 milliseconds.
13274  * 
13275  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13276  * 
13277  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13278  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13279  * Similar to an autorepeat key delay.
13280  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13281  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13282  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13283  *           "interval" and "delay" are ignored. "immediate" is honored.
13284  * @cfg {Boolean} preventDefault True to prevent the default click event
13285  * @cfg {Boolean} stopDefault True to stop the default click event
13286  * 
13287  * @history
13288  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13289  *     2007-02-02 jvs Renamed to ClickRepeater
13290  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13291  *
13292  *  @constructor
13293  * @param {String/HTMLElement/Element} el The element to listen on
13294  * @param {Object} config
13295  **/
13296 Roo.util.ClickRepeater = function(el, config)
13297 {
13298     this.el = Roo.get(el);
13299     this.el.unselectable();
13300
13301     Roo.apply(this, config);
13302
13303     this.addEvents({
13304     /**
13305      * @event mousedown
13306      * Fires when the mouse button is depressed.
13307      * @param {Roo.util.ClickRepeater} this
13308      */
13309         "mousedown" : true,
13310     /**
13311      * @event click
13312      * Fires on a specified interval during the time the element is pressed.
13313      * @param {Roo.util.ClickRepeater} this
13314      */
13315         "click" : true,
13316     /**
13317      * @event mouseup
13318      * Fires when the mouse key is released.
13319      * @param {Roo.util.ClickRepeater} this
13320      */
13321         "mouseup" : true
13322     });
13323
13324     this.el.on("mousedown", this.handleMouseDown, this);
13325     if(this.preventDefault || this.stopDefault){
13326         this.el.on("click", function(e){
13327             if(this.preventDefault){
13328                 e.preventDefault();
13329             }
13330             if(this.stopDefault){
13331                 e.stopEvent();
13332             }
13333         }, this);
13334     }
13335
13336     // allow inline handler
13337     if(this.handler){
13338         this.on("click", this.handler,  this.scope || this);
13339     }
13340
13341     Roo.util.ClickRepeater.superclass.constructor.call(this);
13342 };
13343
13344 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13345     interval : 20,
13346     delay: 250,
13347     preventDefault : true,
13348     stopDefault : false,
13349     timer : 0,
13350
13351     // private
13352     handleMouseDown : function(){
13353         clearTimeout(this.timer);
13354         this.el.blur();
13355         if(this.pressClass){
13356             this.el.addClass(this.pressClass);
13357         }
13358         this.mousedownTime = new Date();
13359
13360         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13361         this.el.on("mouseout", this.handleMouseOut, this);
13362
13363         this.fireEvent("mousedown", this);
13364         this.fireEvent("click", this);
13365         
13366         this.timer = this.click.defer(this.delay || this.interval, this);
13367     },
13368
13369     // private
13370     click : function(){
13371         this.fireEvent("click", this);
13372         this.timer = this.click.defer(this.getInterval(), this);
13373     },
13374
13375     // private
13376     getInterval: function(){
13377         if(!this.accelerate){
13378             return this.interval;
13379         }
13380         var pressTime = this.mousedownTime.getElapsed();
13381         if(pressTime < 500){
13382             return 400;
13383         }else if(pressTime < 1700){
13384             return 320;
13385         }else if(pressTime < 2600){
13386             return 250;
13387         }else if(pressTime < 3500){
13388             return 180;
13389         }else if(pressTime < 4400){
13390             return 140;
13391         }else if(pressTime < 5300){
13392             return 80;
13393         }else if(pressTime < 6200){
13394             return 50;
13395         }else{
13396             return 10;
13397         }
13398     },
13399
13400     // private
13401     handleMouseOut : function(){
13402         clearTimeout(this.timer);
13403         if(this.pressClass){
13404             this.el.removeClass(this.pressClass);
13405         }
13406         this.el.on("mouseover", this.handleMouseReturn, this);
13407     },
13408
13409     // private
13410     handleMouseReturn : function(){
13411         this.el.un("mouseover", this.handleMouseReturn);
13412         if(this.pressClass){
13413             this.el.addClass(this.pressClass);
13414         }
13415         this.click();
13416     },
13417
13418     // private
13419     handleMouseUp : function(){
13420         clearTimeout(this.timer);
13421         this.el.un("mouseover", this.handleMouseReturn);
13422         this.el.un("mouseout", this.handleMouseOut);
13423         Roo.get(document).un("mouseup", this.handleMouseUp);
13424         this.el.removeClass(this.pressClass);
13425         this.fireEvent("mouseup", this);
13426     }
13427 });/*
13428  * Based on:
13429  * Ext JS Library 1.1.1
13430  * Copyright(c) 2006-2007, Ext JS, LLC.
13431  *
13432  * Originally Released Under LGPL - original licence link has changed is not relivant.
13433  *
13434  * Fork - LGPL
13435  * <script type="text/javascript">
13436  */
13437
13438  
13439 /**
13440  * @class Roo.KeyNav
13441  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13442  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13443  * way to implement custom navigation schemes for any UI component.</p>
13444  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13445  * pageUp, pageDown, del, home, end.  Usage:</p>
13446  <pre><code>
13447 var nav = new Roo.KeyNav("my-element", {
13448     "left" : function(e){
13449         this.moveLeft(e.ctrlKey);
13450     },
13451     "right" : function(e){
13452         this.moveRight(e.ctrlKey);
13453     },
13454     "enter" : function(e){
13455         this.save();
13456     },
13457     scope : this
13458 });
13459 </code></pre>
13460  * @constructor
13461  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13462  * @param {Object} config The config
13463  */
13464 Roo.KeyNav = function(el, config){
13465     this.el = Roo.get(el);
13466     Roo.apply(this, config);
13467     if(!this.disabled){
13468         this.disabled = true;
13469         this.enable();
13470     }
13471 };
13472
13473 Roo.KeyNav.prototype = {
13474     /**
13475      * @cfg {Boolean} disabled
13476      * True to disable this KeyNav instance (defaults to false)
13477      */
13478     disabled : false,
13479     /**
13480      * @cfg {String} defaultEventAction
13481      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13482      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13483      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13484      */
13485     defaultEventAction: "stopEvent",
13486     /**
13487      * @cfg {Boolean} forceKeyDown
13488      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13489      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13490      * handle keydown instead of keypress.
13491      */
13492     forceKeyDown : false,
13493
13494     // private
13495     prepareEvent : function(e){
13496         var k = e.getKey();
13497         var h = this.keyToHandler[k];
13498         //if(h && this[h]){
13499         //    e.stopPropagation();
13500         //}
13501         if(Roo.isSafari && h && k >= 37 && k <= 40){
13502             e.stopEvent();
13503         }
13504     },
13505
13506     // private
13507     relay : function(e){
13508         var k = e.getKey();
13509         var h = this.keyToHandler[k];
13510         if(h && this[h]){
13511             if(this.doRelay(e, this[h], h) !== true){
13512                 e[this.defaultEventAction]();
13513             }
13514         }
13515     },
13516
13517     // private
13518     doRelay : function(e, h, hname){
13519         return h.call(this.scope || this, e);
13520     },
13521
13522     // possible handlers
13523     enter : false,
13524     left : false,
13525     right : false,
13526     up : false,
13527     down : false,
13528     tab : false,
13529     esc : false,
13530     pageUp : false,
13531     pageDown : false,
13532     del : false,
13533     home : false,
13534     end : false,
13535
13536     // quick lookup hash
13537     keyToHandler : {
13538         37 : "left",
13539         39 : "right",
13540         38 : "up",
13541         40 : "down",
13542         33 : "pageUp",
13543         34 : "pageDown",
13544         46 : "del",
13545         36 : "home",
13546         35 : "end",
13547         13 : "enter",
13548         27 : "esc",
13549         9  : "tab"
13550     },
13551
13552         /**
13553          * Enable this KeyNav
13554          */
13555         enable: function(){
13556                 if(this.disabled){
13557             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13558             // the EventObject will normalize Safari automatically
13559             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13560                 this.el.on("keydown", this.relay,  this);
13561             }else{
13562                 this.el.on("keydown", this.prepareEvent,  this);
13563                 this.el.on("keypress", this.relay,  this);
13564             }
13565                     this.disabled = false;
13566                 }
13567         },
13568
13569         /**
13570          * Disable this KeyNav
13571          */
13572         disable: function(){
13573                 if(!this.disabled){
13574                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13575                 this.el.un("keydown", this.relay);
13576             }else{
13577                 this.el.un("keydown", this.prepareEvent);
13578                 this.el.un("keypress", this.relay);
13579             }
13580                     this.disabled = true;
13581                 }
13582         }
13583 };/*
13584  * Based on:
13585  * Ext JS Library 1.1.1
13586  * Copyright(c) 2006-2007, Ext JS, LLC.
13587  *
13588  * Originally Released Under LGPL - original licence link has changed is not relivant.
13589  *
13590  * Fork - LGPL
13591  * <script type="text/javascript">
13592  */
13593
13594  
13595 /**
13596  * @class Roo.KeyMap
13597  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13598  * The constructor accepts the same config object as defined by {@link #addBinding}.
13599  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13600  * combination it will call the function with this signature (if the match is a multi-key
13601  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13602  * A KeyMap can also handle a string representation of keys.<br />
13603  * Usage:
13604  <pre><code>
13605 // map one key by key code
13606 var map = new Roo.KeyMap("my-element", {
13607     key: 13, // or Roo.EventObject.ENTER
13608     fn: myHandler,
13609     scope: myObject
13610 });
13611
13612 // map multiple keys to one action by string
13613 var map = new Roo.KeyMap("my-element", {
13614     key: "a\r\n\t",
13615     fn: myHandler,
13616     scope: myObject
13617 });
13618
13619 // map multiple keys to multiple actions by strings and array of codes
13620 var map = new Roo.KeyMap("my-element", [
13621     {
13622         key: [10,13],
13623         fn: function(){ alert("Return was pressed"); }
13624     }, {
13625         key: "abc",
13626         fn: function(){ alert('a, b or c was pressed'); }
13627     }, {
13628         key: "\t",
13629         ctrl:true,
13630         shift:true,
13631         fn: function(){ alert('Control + shift + tab was pressed.'); }
13632     }
13633 ]);
13634 </code></pre>
13635  * <b>Note: A KeyMap starts enabled</b>
13636  * @constructor
13637  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13638  * @param {Object} config The config (see {@link #addBinding})
13639  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13640  */
13641 Roo.KeyMap = function(el, config, eventName){
13642     this.el  = Roo.get(el);
13643     this.eventName = eventName || "keydown";
13644     this.bindings = [];
13645     if(config){
13646         this.addBinding(config);
13647     }
13648     this.enable();
13649 };
13650
13651 Roo.KeyMap.prototype = {
13652     /**
13653      * True to stop the event from bubbling and prevent the default browser action if the
13654      * key was handled by the KeyMap (defaults to false)
13655      * @type Boolean
13656      */
13657     stopEvent : false,
13658
13659     /**
13660      * Add a new binding to this KeyMap. The following config object properties are supported:
13661      * <pre>
13662 Property    Type             Description
13663 ----------  ---------------  ----------------------------------------------------------------------
13664 key         String/Array     A single keycode or an array of keycodes to handle
13665 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13666 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13667 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13668 fn          Function         The function to call when KeyMap finds the expected key combination
13669 scope       Object           The scope of the callback function
13670 </pre>
13671      *
13672      * Usage:
13673      * <pre><code>
13674 // Create a KeyMap
13675 var map = new Roo.KeyMap(document, {
13676     key: Roo.EventObject.ENTER,
13677     fn: handleKey,
13678     scope: this
13679 });
13680
13681 //Add a new binding to the existing KeyMap later
13682 map.addBinding({
13683     key: 'abc',
13684     shift: true,
13685     fn: handleKey,
13686     scope: this
13687 });
13688 </code></pre>
13689      * @param {Object/Array} config A single KeyMap config or an array of configs
13690      */
13691         addBinding : function(config){
13692         if(config instanceof Array){
13693             for(var i = 0, len = config.length; i < len; i++){
13694                 this.addBinding(config[i]);
13695             }
13696             return;
13697         }
13698         var keyCode = config.key,
13699             shift = config.shift, 
13700             ctrl = config.ctrl, 
13701             alt = config.alt,
13702             fn = config.fn,
13703             scope = config.scope;
13704         if(typeof keyCode == "string"){
13705             var ks = [];
13706             var keyString = keyCode.toUpperCase();
13707             for(var j = 0, len = keyString.length; j < len; j++){
13708                 ks.push(keyString.charCodeAt(j));
13709             }
13710             keyCode = ks;
13711         }
13712         var keyArray = keyCode instanceof Array;
13713         var handler = function(e){
13714             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13715                 var k = e.getKey();
13716                 if(keyArray){
13717                     for(var i = 0, len = keyCode.length; i < len; i++){
13718                         if(keyCode[i] == k){
13719                           if(this.stopEvent){
13720                               e.stopEvent();
13721                           }
13722                           fn.call(scope || window, k, e);
13723                           return;
13724                         }
13725                     }
13726                 }else{
13727                     if(k == keyCode){
13728                         if(this.stopEvent){
13729                            e.stopEvent();
13730                         }
13731                         fn.call(scope || window, k, e);
13732                     }
13733                 }
13734             }
13735         };
13736         this.bindings.push(handler);  
13737         },
13738
13739     /**
13740      * Shorthand for adding a single key listener
13741      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13742      * following options:
13743      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13744      * @param {Function} fn The function to call
13745      * @param {Object} scope (optional) The scope of the function
13746      */
13747     on : function(key, fn, scope){
13748         var keyCode, shift, ctrl, alt;
13749         if(typeof key == "object" && !(key instanceof Array)){
13750             keyCode = key.key;
13751             shift = key.shift;
13752             ctrl = key.ctrl;
13753             alt = key.alt;
13754         }else{
13755             keyCode = key;
13756         }
13757         this.addBinding({
13758             key: keyCode,
13759             shift: shift,
13760             ctrl: ctrl,
13761             alt: alt,
13762             fn: fn,
13763             scope: scope
13764         })
13765     },
13766
13767     // private
13768     handleKeyDown : function(e){
13769             if(this.enabled){ //just in case
13770             var b = this.bindings;
13771             for(var i = 0, len = b.length; i < len; i++){
13772                 b[i].call(this, e);
13773             }
13774             }
13775         },
13776         
13777         /**
13778          * Returns true if this KeyMap is enabled
13779          * @return {Boolean} 
13780          */
13781         isEnabled : function(){
13782             return this.enabled;  
13783         },
13784         
13785         /**
13786          * Enables this KeyMap
13787          */
13788         enable: function(){
13789                 if(!this.enabled){
13790                     this.el.on(this.eventName, this.handleKeyDown, this);
13791                     this.enabled = true;
13792                 }
13793         },
13794
13795         /**
13796          * Disable this KeyMap
13797          */
13798         disable: function(){
13799                 if(this.enabled){
13800                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13801                     this.enabled = false;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.util.TextMetrics
13818  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13819  * wide, in pixels, a given block of text will be.
13820  * @singleton
13821  */
13822 Roo.util.TextMetrics = function(){
13823     var shared;
13824     return {
13825         /**
13826          * Measures the size of the specified text
13827          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13828          * that can affect the size of the rendered text
13829          * @param {String} text The text to measure
13830          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13831          * in order to accurately measure the text height
13832          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13833          */
13834         measure : function(el, text, fixedWidth){
13835             if(!shared){
13836                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13837             }
13838             shared.bind(el);
13839             shared.setFixedWidth(fixedWidth || 'auto');
13840             return shared.getSize(text);
13841         },
13842
13843         /**
13844          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13845          * the overhead of multiple calls to initialize the style properties on each measurement.
13846          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13847          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13848          * in order to accurately measure the text height
13849          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13850          */
13851         createInstance : function(el, fixedWidth){
13852             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13853         }
13854     };
13855 }();
13856
13857 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13858     var ml = new Roo.Element(document.createElement('div'));
13859     document.body.appendChild(ml.dom);
13860     ml.position('absolute');
13861     ml.setLeftTop(-1000, -1000);
13862     ml.hide();
13863
13864     if(fixedWidth){
13865         ml.setWidth(fixedWidth);
13866     }
13867
13868     var instance = {
13869         /**
13870          * Returns the size of the specified text based on the internal element's style and width properties
13871          * @param {String} text The text to measure
13872          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13873          */
13874         getSize : function(text){
13875             ml.update(text);
13876             var s = ml.getSize();
13877             ml.update('');
13878             return s;
13879         },
13880
13881         /**
13882          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13883          * that can affect the size of the rendered text
13884          * @param {String/HTMLElement} el The element, dom node or id
13885          */
13886         bind : function(el){
13887             ml.setStyle(
13888                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13889             );
13890         },
13891
13892         /**
13893          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13894          * to set a fixed width in order to accurately measure the text height.
13895          * @param {Number} width The width to set on the element
13896          */
13897         setFixedWidth : function(width){
13898             ml.setWidth(width);
13899         },
13900
13901         /**
13902          * Returns the measured width of the specified text
13903          * @param {String} text The text to measure
13904          * @return {Number} width The width in pixels
13905          */
13906         getWidth : function(text){
13907             ml.dom.style.width = 'auto';
13908             return this.getSize(text).width;
13909         },
13910
13911         /**
13912          * Returns the measured height of the specified text.  For multiline text, be sure to call
13913          * {@link #setFixedWidth} if necessary.
13914          * @param {String} text The text to measure
13915          * @return {Number} height The height in pixels
13916          */
13917         getHeight : function(text){
13918             return this.getSize(text).height;
13919         }
13920     };
13921
13922     instance.bind(bindTo);
13923
13924     return instance;
13925 };
13926
13927 // backwards compat
13928 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13929  * Based on:
13930  * Ext JS Library 1.1.1
13931  * Copyright(c) 2006-2007, Ext JS, LLC.
13932  *
13933  * Originally Released Under LGPL - original licence link has changed is not relivant.
13934  *
13935  * Fork - LGPL
13936  * <script type="text/javascript">
13937  */
13938
13939 /**
13940  * @class Roo.state.Provider
13941  * Abstract base class for state provider implementations. This class provides methods
13942  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13943  * Provider interface.
13944  */
13945 Roo.state.Provider = function(){
13946     /**
13947      * @event statechange
13948      * Fires when a state change occurs.
13949      * @param {Provider} this This state provider
13950      * @param {String} key The state key which was changed
13951      * @param {String} value The encoded value for the state
13952      */
13953     this.addEvents({
13954         "statechange": true
13955     });
13956     this.state = {};
13957     Roo.state.Provider.superclass.constructor.call(this);
13958 };
13959 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13960     /**
13961      * Returns the current value for a key
13962      * @param {String} name The key name
13963      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13964      * @return {Mixed} The state data
13965      */
13966     get : function(name, defaultValue){
13967         return typeof this.state[name] == "undefined" ?
13968             defaultValue : this.state[name];
13969     },
13970     
13971     /**
13972      * Clears a value from the state
13973      * @param {String} name The key name
13974      */
13975     clear : function(name){
13976         delete this.state[name];
13977         this.fireEvent("statechange", this, name, null);
13978     },
13979     
13980     /**
13981      * Sets the value for a key
13982      * @param {String} name The key name
13983      * @param {Mixed} value The value to set
13984      */
13985     set : function(name, value){
13986         this.state[name] = value;
13987         this.fireEvent("statechange", this, name, value);
13988     },
13989     
13990     /**
13991      * Decodes a string previously encoded with {@link #encodeValue}.
13992      * @param {String} value The value to decode
13993      * @return {Mixed} The decoded value
13994      */
13995     decodeValue : function(cookie){
13996         var re = /^(a|n|d|b|s|o)\:(.*)$/;
13997         var matches = re.exec(unescape(cookie));
13998         if(!matches || !matches[1]) return; // non state cookie
13999         var type = matches[1];
14000         var v = matches[2];
14001         switch(type){
14002             case "n":
14003                 return parseFloat(v);
14004             case "d":
14005                 return new Date(Date.parse(v));
14006             case "b":
14007                 return (v == "1");
14008             case "a":
14009                 var all = [];
14010                 var values = v.split("^");
14011                 for(var i = 0, len = values.length; i < len; i++){
14012                     all.push(this.decodeValue(values[i]));
14013                 }
14014                 return all;
14015            case "o":
14016                 var all = {};
14017                 var values = v.split("^");
14018                 for(var i = 0, len = values.length; i < len; i++){
14019                     var kv = values[i].split("=");
14020                     all[kv[0]] = this.decodeValue(kv[1]);
14021                 }
14022                 return all;
14023            default:
14024                 return v;
14025         }
14026     },
14027     
14028     /**
14029      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14030      * @param {Mixed} value The value to encode
14031      * @return {String} The encoded value
14032      */
14033     encodeValue : function(v){
14034         var enc;
14035         if(typeof v == "number"){
14036             enc = "n:" + v;
14037         }else if(typeof v == "boolean"){
14038             enc = "b:" + (v ? "1" : "0");
14039         }else if(v instanceof Date){
14040             enc = "d:" + v.toGMTString();
14041         }else if(v instanceof Array){
14042             var flat = "";
14043             for(var i = 0, len = v.length; i < len; i++){
14044                 flat += this.encodeValue(v[i]);
14045                 if(i != len-1) flat += "^";
14046             }
14047             enc = "a:" + flat;
14048         }else if(typeof v == "object"){
14049             var flat = "";
14050             for(var key in v){
14051                 if(typeof v[key] != "function"){
14052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14053                 }
14054             }
14055             enc = "o:" + flat.substring(0, flat.length-1);
14056         }else{
14057             enc = "s:" + v;
14058         }
14059         return escape(enc);        
14060     }
14061 });
14062
14063 /*
14064  * Based on:
14065  * Ext JS Library 1.1.1
14066  * Copyright(c) 2006-2007, Ext JS, LLC.
14067  *
14068  * Originally Released Under LGPL - original licence link has changed is not relivant.
14069  *
14070  * Fork - LGPL
14071  * <script type="text/javascript">
14072  */
14073 /**
14074  * @class Roo.state.Manager
14075  * This is the global state manager. By default all components that are "state aware" check this class
14076  * for state information if you don't pass them a custom state provider. In order for this class
14077  * to be useful, it must be initialized with a provider when your application initializes.
14078  <pre><code>
14079 // in your initialization function
14080 init : function(){
14081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14082    ...
14083    // supposed you have a {@link Roo.BorderLayout}
14084    var layout = new Roo.BorderLayout(...);
14085    layout.restoreState();
14086    // or a {Roo.BasicDialog}
14087    var dialog = new Roo.BasicDialog(...);
14088    dialog.restoreState();
14089  </code></pre>
14090  * @singleton
14091  */
14092 Roo.state.Manager = function(){
14093     var provider = new Roo.state.Provider();
14094     
14095     return {
14096         /**
14097          * Configures the default state provider for your application
14098          * @param {Provider} stateProvider The state provider to set
14099          */
14100         setProvider : function(stateProvider){
14101             provider = stateProvider;
14102         },
14103         
14104         /**
14105          * Returns the current value for a key
14106          * @param {String} name The key name
14107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14108          * @return {Mixed} The state data
14109          */
14110         get : function(key, defaultValue){
14111             return provider.get(key, defaultValue);
14112         },
14113         
14114         /**
14115          * Sets the value for a key
14116          * @param {String} name The key name
14117          * @param {Mixed} value The state data
14118          */
14119          set : function(key, value){
14120             provider.set(key, value);
14121         },
14122         
14123         /**
14124          * Clears a value from the state
14125          * @param {String} name The key name
14126          */
14127         clear : function(key){
14128             provider.clear(key);
14129         },
14130         
14131         /**
14132          * Gets the currently configured state provider
14133          * @return {Provider} The state provider
14134          */
14135         getProvider : function(){
14136             return provider;
14137         }
14138     };
14139 }();
14140 /*
14141  * Based on:
14142  * Ext JS Library 1.1.1
14143  * Copyright(c) 2006-2007, Ext JS, LLC.
14144  *
14145  * Originally Released Under LGPL - original licence link has changed is not relivant.
14146  *
14147  * Fork - LGPL
14148  * <script type="text/javascript">
14149  */
14150 /**
14151  * @class Roo.state.CookieProvider
14152  * @extends Roo.state.Provider
14153  * The default Provider implementation which saves state via cookies.
14154  * <br />Usage:
14155  <pre><code>
14156    var cp = new Roo.state.CookieProvider({
14157        path: "/cgi-bin/",
14158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14159        domain: "roojs.com"
14160    })
14161    Roo.state.Manager.setProvider(cp);
14162  </code></pre>
14163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14168  * domain the page is running on including the 'www' like 'www.roojs.com')
14169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14170  * @constructor
14171  * Create a new CookieProvider
14172  * @param {Object} config The configuration object
14173  */
14174 Roo.state.CookieProvider = function(config){
14175     Roo.state.CookieProvider.superclass.constructor.call(this);
14176     this.path = "/";
14177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14178     this.domain = null;
14179     this.secure = false;
14180     Roo.apply(this, config);
14181     this.state = this.readCookies();
14182 };
14183
14184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14185     // private
14186     set : function(name, value){
14187         if(typeof value == "undefined" || value === null){
14188             this.clear(name);
14189             return;
14190         }
14191         this.setCookie(name, value);
14192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14193     },
14194
14195     // private
14196     clear : function(name){
14197         this.clearCookie(name);
14198         Roo.state.CookieProvider.superclass.clear.call(this, name);
14199     },
14200
14201     // private
14202     readCookies : function(){
14203         var cookies = {};
14204         var c = document.cookie + ";";
14205         var re = /\s?(.*?)=(.*?);/g;
14206         var matches;
14207         while((matches = re.exec(c)) != null){
14208             var name = matches[1];
14209             var value = matches[2];
14210             if(name && name.substring(0,3) == "ys-"){
14211                 cookies[name.substr(3)] = this.decodeValue(value);
14212             }
14213         }
14214         return cookies;
14215     },
14216
14217     // private
14218     setCookie : function(name, value){
14219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14221            ((this.path == null) ? "" : ("; path=" + this.path)) +
14222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14223            ((this.secure == true) ? "; secure" : "");
14224     },
14225
14226     // private
14227     clearCookie : function(name){
14228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14229            ((this.path == null) ? "" : ("; path=" + this.path)) +
14230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14231            ((this.secure == true) ? "; secure" : "");
14232     }
14233 });/*
14234  * Based on:
14235  * Ext JS Library 1.1.1
14236  * Copyright(c) 2006-2007, Ext JS, LLC.
14237  *
14238  * Originally Released Under LGPL - original licence link has changed is not relivant.
14239  *
14240  * Fork - LGPL
14241  * <script type="text/javascript">
14242  */
14243
14244
14245
14246 /*
14247  * These classes are derivatives of the similarly named classes in the YUI Library.
14248  * The original license:
14249  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14250  * Code licensed under the BSD License:
14251  * http://developer.yahoo.net/yui/license.txt
14252  */
14253
14254 (function() {
14255
14256 var Event=Roo.EventManager;
14257 var Dom=Roo.lib.Dom;
14258
14259 /**
14260  * @class Roo.dd.DragDrop
14261  * Defines the interface and base operation of items that that can be
14262  * dragged or can be drop targets.  It was designed to be extended, overriding
14263  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14264  * Up to three html elements can be associated with a DragDrop instance:
14265  * <ul>
14266  * <li>linked element: the element that is passed into the constructor.
14267  * This is the element which defines the boundaries for interaction with
14268  * other DragDrop objects.</li>
14269  * <li>handle element(s): The drag operation only occurs if the element that
14270  * was clicked matches a handle element.  By default this is the linked
14271  * element, but there are times that you will want only a portion of the
14272  * linked element to initiate the drag operation, and the setHandleElId()
14273  * method provides a way to define this.</li>
14274  * <li>drag element: this represents the element that would be moved along
14275  * with the cursor during a drag operation.  By default, this is the linked
14276  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14277  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14278  * </li>
14279  * </ul>
14280  * This class should not be instantiated until the onload event to ensure that
14281  * the associated elements are available.
14282  * The following would define a DragDrop obj that would interact with any
14283  * other DragDrop obj in the "group1" group:
14284  * <pre>
14285  *  dd = new Roo.dd.DragDrop("div1", "group1");
14286  * </pre>
14287  * Since none of the event handlers have been implemented, nothing would
14288  * actually happen if you were to run the code above.  Normally you would
14289  * override this class or one of the default implementations, but you can
14290  * also override the methods you want on an instance of the class...
14291  * <pre>
14292  *  dd.onDragDrop = function(e, id) {
14293  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14294  *  }
14295  * </pre>
14296  * @constructor
14297  * @param {String} id of the element that is linked to this instance
14298  * @param {String} sGroup the group of related DragDrop objects
14299  * @param {object} config an object containing configurable attributes
14300  *                Valid properties for DragDrop:
14301  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14302  */
14303 Roo.dd.DragDrop = function(id, sGroup, config) {
14304     if (id) {
14305         this.init(id, sGroup, config);
14306     }
14307 };
14308
14309 Roo.dd.DragDrop.prototype = {
14310
14311     /**
14312      * The id of the element associated with this object.  This is what we
14313      * refer to as the "linked element" because the size and position of
14314      * this element is used to determine when the drag and drop objects have
14315      * interacted.
14316      * @property id
14317      * @type String
14318      */
14319     id: null,
14320
14321     /**
14322      * Configuration attributes passed into the constructor
14323      * @property config
14324      * @type object
14325      */
14326     config: null,
14327
14328     /**
14329      * The id of the element that will be dragged.  By default this is same
14330      * as the linked element , but could be changed to another element. Ex:
14331      * Roo.dd.DDProxy
14332      * @property dragElId
14333      * @type String
14334      * @private
14335      */
14336     dragElId: null,
14337
14338     /**
14339      * the id of the element that initiates the drag operation.  By default
14340      * this is the linked element, but could be changed to be a child of this
14341      * element.  This lets us do things like only starting the drag when the
14342      * header element within the linked html element is clicked.
14343      * @property handleElId
14344      * @type String
14345      * @private
14346      */
14347     handleElId: null,
14348
14349     /**
14350      * An associative array of HTML tags that will be ignored if clicked.
14351      * @property invalidHandleTypes
14352      * @type {string: string}
14353      */
14354     invalidHandleTypes: null,
14355
14356     /**
14357      * An associative array of ids for elements that will be ignored if clicked
14358      * @property invalidHandleIds
14359      * @type {string: string}
14360      */
14361     invalidHandleIds: null,
14362
14363     /**
14364      * An indexted array of css class names for elements that will be ignored
14365      * if clicked.
14366      * @property invalidHandleClasses
14367      * @type string[]
14368      */
14369     invalidHandleClasses: null,
14370
14371     /**
14372      * The linked element's absolute X position at the time the drag was
14373      * started
14374      * @property startPageX
14375      * @type int
14376      * @private
14377      */
14378     startPageX: 0,
14379
14380     /**
14381      * The linked element's absolute X position at the time the drag was
14382      * started
14383      * @property startPageY
14384      * @type int
14385      * @private
14386      */
14387     startPageY: 0,
14388
14389     /**
14390      * The group defines a logical collection of DragDrop objects that are
14391      * related.  Instances only get events when interacting with other
14392      * DragDrop object in the same group.  This lets us define multiple
14393      * groups using a single DragDrop subclass if we want.
14394      * @property groups
14395      * @type {string: string}
14396      */
14397     groups: null,
14398
14399     /**
14400      * Individual drag/drop instances can be locked.  This will prevent
14401      * onmousedown start drag.
14402      * @property locked
14403      * @type boolean
14404      * @private
14405      */
14406     locked: false,
14407
14408     /**
14409      * Lock this instance
14410      * @method lock
14411      */
14412     lock: function() { this.locked = true; },
14413
14414     /**
14415      * Unlock this instace
14416      * @method unlock
14417      */
14418     unlock: function() { this.locked = false; },
14419
14420     /**
14421      * By default, all insances can be a drop target.  This can be disabled by
14422      * setting isTarget to false.
14423      * @method isTarget
14424      * @type boolean
14425      */
14426     isTarget: true,
14427
14428     /**
14429      * The padding configured for this drag and drop object for calculating
14430      * the drop zone intersection with this object.
14431      * @method padding
14432      * @type int[]
14433      */
14434     padding: null,
14435
14436     /**
14437      * Cached reference to the linked element
14438      * @property _domRef
14439      * @private
14440      */
14441     _domRef: null,
14442
14443     /**
14444      * Internal typeof flag
14445      * @property __ygDragDrop
14446      * @private
14447      */
14448     __ygDragDrop: true,
14449
14450     /**
14451      * Set to true when horizontal contraints are applied
14452      * @property constrainX
14453      * @type boolean
14454      * @private
14455      */
14456     constrainX: false,
14457
14458     /**
14459      * Set to true when vertical contraints are applied
14460      * @property constrainY
14461      * @type boolean
14462      * @private
14463      */
14464     constrainY: false,
14465
14466     /**
14467      * The left constraint
14468      * @property minX
14469      * @type int
14470      * @private
14471      */
14472     minX: 0,
14473
14474     /**
14475      * The right constraint
14476      * @property maxX
14477      * @type int
14478      * @private
14479      */
14480     maxX: 0,
14481
14482     /**
14483      * The up constraint
14484      * @property minY
14485      * @type int
14486      * @type int
14487      * @private
14488      */
14489     minY: 0,
14490
14491     /**
14492      * The down constraint
14493      * @property maxY
14494      * @type int
14495      * @private
14496      */
14497     maxY: 0,
14498
14499     /**
14500      * Maintain offsets when we resetconstraints.  Set to true when you want
14501      * the position of the element relative to its parent to stay the same
14502      * when the page changes
14503      *
14504      * @property maintainOffset
14505      * @type boolean
14506      */
14507     maintainOffset: false,
14508
14509     /**
14510      * Array of pixel locations the element will snap to if we specified a
14511      * horizontal graduation/interval.  This array is generated automatically
14512      * when you define a tick interval.
14513      * @property xTicks
14514      * @type int[]
14515      */
14516     xTicks: null,
14517
14518     /**
14519      * Array of pixel locations the element will snap to if we specified a
14520      * vertical graduation/interval.  This array is generated automatically
14521      * when you define a tick interval.
14522      * @property yTicks
14523      * @type int[]
14524      */
14525     yTicks: null,
14526
14527     /**
14528      * By default the drag and drop instance will only respond to the primary
14529      * button click (left button for a right-handed mouse).  Set to true to
14530      * allow drag and drop to start with any mouse click that is propogated
14531      * by the browser
14532      * @property primaryButtonOnly
14533      * @type boolean
14534      */
14535     primaryButtonOnly: true,
14536
14537     /**
14538      * The availabe property is false until the linked dom element is accessible.
14539      * @property available
14540      * @type boolean
14541      */
14542     available: false,
14543
14544     /**
14545      * By default, drags can only be initiated if the mousedown occurs in the
14546      * region the linked element is.  This is done in part to work around a
14547      * bug in some browsers that mis-report the mousedown if the previous
14548      * mouseup happened outside of the window.  This property is set to true
14549      * if outer handles are defined.
14550      *
14551      * @property hasOuterHandles
14552      * @type boolean
14553      * @default false
14554      */
14555     hasOuterHandles: false,
14556
14557     /**
14558      * Code that executes immediately before the startDrag event
14559      * @method b4StartDrag
14560      * @private
14561      */
14562     b4StartDrag: function(x, y) { },
14563
14564     /**
14565      * Abstract method called after a drag/drop object is clicked
14566      * and the drag or mousedown time thresholds have beeen met.
14567      * @method startDrag
14568      * @param {int} X click location
14569      * @param {int} Y click location
14570      */
14571     startDrag: function(x, y) { /* override this */ },
14572
14573     /**
14574      * Code that executes immediately before the onDrag event
14575      * @method b4Drag
14576      * @private
14577      */
14578     b4Drag: function(e) { },
14579
14580     /**
14581      * Abstract method called during the onMouseMove event while dragging an
14582      * object.
14583      * @method onDrag
14584      * @param {Event} e the mousemove event
14585      */
14586     onDrag: function(e) { /* override this */ },
14587
14588     /**
14589      * Abstract method called when this element fist begins hovering over
14590      * another DragDrop obj
14591      * @method onDragEnter
14592      * @param {Event} e the mousemove event
14593      * @param {String|DragDrop[]} id In POINT mode, the element
14594      * id this is hovering over.  In INTERSECT mode, an array of one or more
14595      * dragdrop items being hovered over.
14596      */
14597     onDragEnter: function(e, id) { /* override this */ },
14598
14599     /**
14600      * Code that executes immediately before the onDragOver event
14601      * @method b4DragOver
14602      * @private
14603      */
14604     b4DragOver: function(e) { },
14605
14606     /**
14607      * Abstract method called when this element is hovering over another
14608      * DragDrop obj
14609      * @method onDragOver
14610      * @param {Event} e the mousemove event
14611      * @param {String|DragDrop[]} id In POINT mode, the element
14612      * id this is hovering over.  In INTERSECT mode, an array of dd items
14613      * being hovered over.
14614      */
14615     onDragOver: function(e, id) { /* override this */ },
14616
14617     /**
14618      * Code that executes immediately before the onDragOut event
14619      * @method b4DragOut
14620      * @private
14621      */
14622     b4DragOut: function(e) { },
14623
14624     /**
14625      * Abstract method called when we are no longer hovering over an element
14626      * @method onDragOut
14627      * @param {Event} e the mousemove event
14628      * @param {String|DragDrop[]} id In POINT mode, the element
14629      * id this was hovering over.  In INTERSECT mode, an array of dd items
14630      * that the mouse is no longer over.
14631      */
14632     onDragOut: function(e, id) { /* override this */ },
14633
14634     /**
14635      * Code that executes immediately before the onDragDrop event
14636      * @method b4DragDrop
14637      * @private
14638      */
14639     b4DragDrop: function(e) { },
14640
14641     /**
14642      * Abstract method called when this item is dropped on another DragDrop
14643      * obj
14644      * @method onDragDrop
14645      * @param {Event} e the mouseup event
14646      * @param {String|DragDrop[]} id In POINT mode, the element
14647      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14648      * was dropped on.
14649      */
14650     onDragDrop: function(e, id) { /* override this */ },
14651
14652     /**
14653      * Abstract method called when this item is dropped on an area with no
14654      * drop target
14655      * @method onInvalidDrop
14656      * @param {Event} e the mouseup event
14657      */
14658     onInvalidDrop: function(e) { /* override this */ },
14659
14660     /**
14661      * Code that executes immediately before the endDrag event
14662      * @method b4EndDrag
14663      * @private
14664      */
14665     b4EndDrag: function(e) { },
14666
14667     /**
14668      * Fired when we are done dragging the object
14669      * @method endDrag
14670      * @param {Event} e the mouseup event
14671      */
14672     endDrag: function(e) { /* override this */ },
14673
14674     /**
14675      * Code executed immediately before the onMouseDown event
14676      * @method b4MouseDown
14677      * @param {Event} e the mousedown event
14678      * @private
14679      */
14680     b4MouseDown: function(e) {  },
14681
14682     /**
14683      * Event handler that fires when a drag/drop obj gets a mousedown
14684      * @method onMouseDown
14685      * @param {Event} e the mousedown event
14686      */
14687     onMouseDown: function(e) { /* override this */ },
14688
14689     /**
14690      * Event handler that fires when a drag/drop obj gets a mouseup
14691      * @method onMouseUp
14692      * @param {Event} e the mouseup event
14693      */
14694     onMouseUp: function(e) { /* override this */ },
14695
14696     /**
14697      * Override the onAvailable method to do what is needed after the initial
14698      * position was determined.
14699      * @method onAvailable
14700      */
14701     onAvailable: function () {
14702     },
14703
14704     /*
14705      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14706      * @type Object
14707      */
14708     defaultPadding : {left:0, right:0, top:0, bottom:0},
14709
14710     /*
14711      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14712  *
14713  * Usage:
14714  <pre><code>
14715  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14716                 { dragElId: "existingProxyDiv" });
14717  dd.startDrag = function(){
14718      this.constrainTo("parent-id");
14719  };
14720  </code></pre>
14721  * Or you can initalize it using the {@link Roo.Element} object:
14722  <pre><code>
14723  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14724      startDrag : function(){
14725          this.constrainTo("parent-id");
14726      }
14727  });
14728  </code></pre>
14729      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14730      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14731      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14732      * an object containing the sides to pad. For example: {right:10, bottom:10}
14733      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14734      */
14735     constrainTo : function(constrainTo, pad, inContent){
14736         if(typeof pad == "number"){
14737             pad = {left: pad, right:pad, top:pad, bottom:pad};
14738         }
14739         pad = pad || this.defaultPadding;
14740         var b = Roo.get(this.getEl()).getBox();
14741         var ce = Roo.get(constrainTo);
14742         var s = ce.getScroll();
14743         var c, cd = ce.dom;
14744         if(cd == document.body){
14745             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14746         }else{
14747             xy = ce.getXY();
14748             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14749         }
14750
14751
14752         var topSpace = b.y - c.y;
14753         var leftSpace = b.x - c.x;
14754
14755         this.resetConstraints();
14756         this.setXConstraint(leftSpace - (pad.left||0), // left
14757                 c.width - leftSpace - b.width - (pad.right||0) //right
14758         );
14759         this.setYConstraint(topSpace - (pad.top||0), //top
14760                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14761         );
14762     },
14763
14764     /**
14765      * Returns a reference to the linked element
14766      * @method getEl
14767      * @return {HTMLElement} the html element
14768      */
14769     getEl: function() {
14770         if (!this._domRef) {
14771             this._domRef = Roo.getDom(this.id);
14772         }
14773
14774         return this._domRef;
14775     },
14776
14777     /**
14778      * Returns a reference to the actual element to drag.  By default this is
14779      * the same as the html element, but it can be assigned to another
14780      * element. An example of this can be found in Roo.dd.DDProxy
14781      * @method getDragEl
14782      * @return {HTMLElement} the html element
14783      */
14784     getDragEl: function() {
14785         return Roo.getDom(this.dragElId);
14786     },
14787
14788     /**
14789      * Sets up the DragDrop object.  Must be called in the constructor of any
14790      * Roo.dd.DragDrop subclass
14791      * @method init
14792      * @param id the id of the linked element
14793      * @param {String} sGroup the group of related items
14794      * @param {object} config configuration attributes
14795      */
14796     init: function(id, sGroup, config) {
14797         this.initTarget(id, sGroup, config);
14798         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14799         // Event.on(this.id, "selectstart", Event.preventDefault);
14800     },
14801
14802     /**
14803      * Initializes Targeting functionality only... the object does not
14804      * get a mousedown handler.
14805      * @method initTarget
14806      * @param id the id of the linked element
14807      * @param {String} sGroup the group of related items
14808      * @param {object} config configuration attributes
14809      */
14810     initTarget: function(id, sGroup, config) {
14811
14812         // configuration attributes
14813         this.config = config || {};
14814
14815         // create a local reference to the drag and drop manager
14816         this.DDM = Roo.dd.DDM;
14817         // initialize the groups array
14818         this.groups = {};
14819
14820         // assume that we have an element reference instead of an id if the
14821         // parameter is not a string
14822         if (typeof id !== "string") {
14823             id = Roo.id(id);
14824         }
14825
14826         // set the id
14827         this.id = id;
14828
14829         // add to an interaction group
14830         this.addToGroup((sGroup) ? sGroup : "default");
14831
14832         // We don't want to register this as the handle with the manager
14833         // so we just set the id rather than calling the setter.
14834         this.handleElId = id;
14835
14836         // the linked element is the element that gets dragged by default
14837         this.setDragElId(id);
14838
14839         // by default, clicked anchors will not start drag operations.
14840         this.invalidHandleTypes = { A: "A" };
14841         this.invalidHandleIds = {};
14842         this.invalidHandleClasses = [];
14843
14844         this.applyConfig();
14845
14846         this.handleOnAvailable();
14847     },
14848
14849     /**
14850      * Applies the configuration parameters that were passed into the constructor.
14851      * This is supposed to happen at each level through the inheritance chain.  So
14852      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14853      * DragDrop in order to get all of the parameters that are available in
14854      * each object.
14855      * @method applyConfig
14856      */
14857     applyConfig: function() {
14858
14859         // configurable properties:
14860         //    padding, isTarget, maintainOffset, primaryButtonOnly
14861         this.padding           = this.config.padding || [0, 0, 0, 0];
14862         this.isTarget          = (this.config.isTarget !== false);
14863         this.maintainOffset    = (this.config.maintainOffset);
14864         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14865
14866     },
14867
14868     /**
14869      * Executed when the linked element is available
14870      * @method handleOnAvailable
14871      * @private
14872      */
14873     handleOnAvailable: function() {
14874         this.available = true;
14875         this.resetConstraints();
14876         this.onAvailable();
14877     },
14878
14879      /**
14880      * Configures the padding for the target zone in px.  Effectively expands
14881      * (or reduces) the virtual object size for targeting calculations.
14882      * Supports css-style shorthand; if only one parameter is passed, all sides
14883      * will have that padding, and if only two are passed, the top and bottom
14884      * will have the first param, the left and right the second.
14885      * @method setPadding
14886      * @param {int} iTop    Top pad
14887      * @param {int} iRight  Right pad
14888      * @param {int} iBot    Bot pad
14889      * @param {int} iLeft   Left pad
14890      */
14891     setPadding: function(iTop, iRight, iBot, iLeft) {
14892         // this.padding = [iLeft, iRight, iTop, iBot];
14893         if (!iRight && 0 !== iRight) {
14894             this.padding = [iTop, iTop, iTop, iTop];
14895         } else if (!iBot && 0 !== iBot) {
14896             this.padding = [iTop, iRight, iTop, iRight];
14897         } else {
14898             this.padding = [iTop, iRight, iBot, iLeft];
14899         }
14900     },
14901
14902     /**
14903      * Stores the initial placement of the linked element.
14904      * @method setInitialPosition
14905      * @param {int} diffX   the X offset, default 0
14906      * @param {int} diffY   the Y offset, default 0
14907      */
14908     setInitPosition: function(diffX, diffY) {
14909         var el = this.getEl();
14910
14911         if (!this.DDM.verifyEl(el)) {
14912             return;
14913         }
14914
14915         var dx = diffX || 0;
14916         var dy = diffY || 0;
14917
14918         var p = Dom.getXY( el );
14919
14920         this.initPageX = p[0] - dx;
14921         this.initPageY = p[1] - dy;
14922
14923         this.lastPageX = p[0];
14924         this.lastPageY = p[1];
14925
14926
14927         this.setStartPosition(p);
14928     },
14929
14930     /**
14931      * Sets the start position of the element.  This is set when the obj
14932      * is initialized, the reset when a drag is started.
14933      * @method setStartPosition
14934      * @param pos current position (from previous lookup)
14935      * @private
14936      */
14937     setStartPosition: function(pos) {
14938         var p = pos || Dom.getXY( this.getEl() );
14939         this.deltaSetXY = null;
14940
14941         this.startPageX = p[0];
14942         this.startPageY = p[1];
14943     },
14944
14945     /**
14946      * Add this instance to a group of related drag/drop objects.  All
14947      * instances belong to at least one group, and can belong to as many
14948      * groups as needed.
14949      * @method addToGroup
14950      * @param sGroup {string} the name of the group
14951      */
14952     addToGroup: function(sGroup) {
14953         this.groups[sGroup] = true;
14954         this.DDM.regDragDrop(this, sGroup);
14955     },
14956
14957     /**
14958      * Remove's this instance from the supplied interaction group
14959      * @method removeFromGroup
14960      * @param {string}  sGroup  The group to drop
14961      */
14962     removeFromGroup: function(sGroup) {
14963         if (this.groups[sGroup]) {
14964             delete this.groups[sGroup];
14965         }
14966
14967         this.DDM.removeDDFromGroup(this, sGroup);
14968     },
14969
14970     /**
14971      * Allows you to specify that an element other than the linked element
14972      * will be moved with the cursor during a drag
14973      * @method setDragElId
14974      * @param id {string} the id of the element that will be used to initiate the drag
14975      */
14976     setDragElId: function(id) {
14977         this.dragElId = id;
14978     },
14979
14980     /**
14981      * Allows you to specify a child of the linked element that should be
14982      * used to initiate the drag operation.  An example of this would be if
14983      * you have a content div with text and links.  Clicking anywhere in the
14984      * content area would normally start the drag operation.  Use this method
14985      * to specify that an element inside of the content div is the element
14986      * that starts the drag operation.
14987      * @method setHandleElId
14988      * @param id {string} the id of the element that will be used to
14989      * initiate the drag.
14990      */
14991     setHandleElId: function(id) {
14992         if (typeof id !== "string") {
14993             id = Roo.id(id);
14994         }
14995         this.handleElId = id;
14996         this.DDM.regHandle(this.id, id);
14997     },
14998
14999     /**
15000      * Allows you to set an element outside of the linked element as a drag
15001      * handle
15002      * @method setOuterHandleElId
15003      * @param id the id of the element that will be used to initiate the drag
15004      */
15005     setOuterHandleElId: function(id) {
15006         if (typeof id !== "string") {
15007             id = Roo.id(id);
15008         }
15009         Event.on(id, "mousedown",
15010                 this.handleMouseDown, this);
15011         this.setHandleElId(id);
15012
15013         this.hasOuterHandles = true;
15014     },
15015
15016     /**
15017      * Remove all drag and drop hooks for this element
15018      * @method unreg
15019      */
15020     unreg: function() {
15021         Event.un(this.id, "mousedown",
15022                 this.handleMouseDown);
15023         this._domRef = null;
15024         this.DDM._remove(this);
15025     },
15026
15027     destroy : function(){
15028         this.unreg();
15029     },
15030
15031     /**
15032      * Returns true if this instance is locked, or the drag drop mgr is locked
15033      * (meaning that all drag/drop is disabled on the page.)
15034      * @method isLocked
15035      * @return {boolean} true if this obj or all drag/drop is locked, else
15036      * false
15037      */
15038     isLocked: function() {
15039         return (this.DDM.isLocked() || this.locked);
15040     },
15041
15042     /**
15043      * Fired when this object is clicked
15044      * @method handleMouseDown
15045      * @param {Event} e
15046      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15047      * @private
15048      */
15049     handleMouseDown: function(e, oDD){
15050         if (this.primaryButtonOnly && e.button != 0) {
15051             return;
15052         }
15053
15054         if (this.isLocked()) {
15055             return;
15056         }
15057
15058         this.DDM.refreshCache(this.groups);
15059
15060         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15061         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15062         } else {
15063             if (this.clickValidator(e)) {
15064
15065                 // set the initial element position
15066                 this.setStartPosition();
15067
15068
15069                 this.b4MouseDown(e);
15070                 this.onMouseDown(e);
15071
15072                 this.DDM.handleMouseDown(e, this);
15073
15074                 this.DDM.stopEvent(e);
15075             } else {
15076
15077
15078             }
15079         }
15080     },
15081
15082     clickValidator: function(e) {
15083         var target = e.getTarget();
15084         return ( this.isValidHandleChild(target) &&
15085                     (this.id == this.handleElId ||
15086                         this.DDM.handleWasClicked(target, this.id)) );
15087     },
15088
15089     /**
15090      * Allows you to specify a tag name that should not start a drag operation
15091      * when clicked.  This is designed to facilitate embedding links within a
15092      * drag handle that do something other than start the drag.
15093      * @method addInvalidHandleType
15094      * @param {string} tagName the type of element to exclude
15095      */
15096     addInvalidHandleType: function(tagName) {
15097         var type = tagName.toUpperCase();
15098         this.invalidHandleTypes[type] = type;
15099     },
15100
15101     /**
15102      * Lets you to specify an element id for a child of a drag handle
15103      * that should not initiate a drag
15104      * @method addInvalidHandleId
15105      * @param {string} id the element id of the element you wish to ignore
15106      */
15107     addInvalidHandleId: function(id) {
15108         if (typeof id !== "string") {
15109             id = Roo.id(id);
15110         }
15111         this.invalidHandleIds[id] = id;
15112     },
15113
15114     /**
15115      * Lets you specify a css class of elements that will not initiate a drag
15116      * @method addInvalidHandleClass
15117      * @param {string} cssClass the class of the elements you wish to ignore
15118      */
15119     addInvalidHandleClass: function(cssClass) {
15120         this.invalidHandleClasses.push(cssClass);
15121     },
15122
15123     /**
15124      * Unsets an excluded tag name set by addInvalidHandleType
15125      * @method removeInvalidHandleType
15126      * @param {string} tagName the type of element to unexclude
15127      */
15128     removeInvalidHandleType: function(tagName) {
15129         var type = tagName.toUpperCase();
15130         // this.invalidHandleTypes[type] = null;
15131         delete this.invalidHandleTypes[type];
15132     },
15133
15134     /**
15135      * Unsets an invalid handle id
15136      * @method removeInvalidHandleId
15137      * @param {string} id the id of the element to re-enable
15138      */
15139     removeInvalidHandleId: function(id) {
15140         if (typeof id !== "string") {
15141             id = Roo.id(id);
15142         }
15143         delete this.invalidHandleIds[id];
15144     },
15145
15146     /**
15147      * Unsets an invalid css class
15148      * @method removeInvalidHandleClass
15149      * @param {string} cssClass the class of the element(s) you wish to
15150      * re-enable
15151      */
15152     removeInvalidHandleClass: function(cssClass) {
15153         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15154             if (this.invalidHandleClasses[i] == cssClass) {
15155                 delete this.invalidHandleClasses[i];
15156             }
15157         }
15158     },
15159
15160     /**
15161      * Checks the tag exclusion list to see if this click should be ignored
15162      * @method isValidHandleChild
15163      * @param {HTMLElement} node the HTMLElement to evaluate
15164      * @return {boolean} true if this is a valid tag type, false if not
15165      */
15166     isValidHandleChild: function(node) {
15167
15168         var valid = true;
15169         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15170         var nodeName;
15171         try {
15172             nodeName = node.nodeName.toUpperCase();
15173         } catch(e) {
15174             nodeName = node.nodeName;
15175         }
15176         valid = valid && !this.invalidHandleTypes[nodeName];
15177         valid = valid && !this.invalidHandleIds[node.id];
15178
15179         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15180             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15181         }
15182
15183
15184         return valid;
15185
15186     },
15187
15188     /**
15189      * Create the array of horizontal tick marks if an interval was specified
15190      * in setXConstraint().
15191      * @method setXTicks
15192      * @private
15193      */
15194     setXTicks: function(iStartX, iTickSize) {
15195         this.xTicks = [];
15196         this.xTickSize = iTickSize;
15197
15198         var tickMap = {};
15199
15200         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15201             if (!tickMap[i]) {
15202                 this.xTicks[this.xTicks.length] = i;
15203                 tickMap[i] = true;
15204             }
15205         }
15206
15207         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15208             if (!tickMap[i]) {
15209                 this.xTicks[this.xTicks.length] = i;
15210                 tickMap[i] = true;
15211             }
15212         }
15213
15214         this.xTicks.sort(this.DDM.numericSort) ;
15215     },
15216
15217     /**
15218      * Create the array of vertical tick marks if an interval was specified in
15219      * setYConstraint().
15220      * @method setYTicks
15221      * @private
15222      */
15223     setYTicks: function(iStartY, iTickSize) {
15224         this.yTicks = [];
15225         this.yTickSize = iTickSize;
15226
15227         var tickMap = {};
15228
15229         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15230             if (!tickMap[i]) {
15231                 this.yTicks[this.yTicks.length] = i;
15232                 tickMap[i] = true;
15233             }
15234         }
15235
15236         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15237             if (!tickMap[i]) {
15238                 this.yTicks[this.yTicks.length] = i;
15239                 tickMap[i] = true;
15240             }
15241         }
15242
15243         this.yTicks.sort(this.DDM.numericSort) ;
15244     },
15245
15246     /**
15247      * By default, the element can be dragged any place on the screen.  Use
15248      * this method to limit the horizontal travel of the element.  Pass in
15249      * 0,0 for the parameters if you want to lock the drag to the y axis.
15250      * @method setXConstraint
15251      * @param {int} iLeft the number of pixels the element can move to the left
15252      * @param {int} iRight the number of pixels the element can move to the
15253      * right
15254      * @param {int} iTickSize optional parameter for specifying that the
15255      * element
15256      * should move iTickSize pixels at a time.
15257      */
15258     setXConstraint: function(iLeft, iRight, iTickSize) {
15259         this.leftConstraint = iLeft;
15260         this.rightConstraint = iRight;
15261
15262         this.minX = this.initPageX - iLeft;
15263         this.maxX = this.initPageX + iRight;
15264         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15265
15266         this.constrainX = true;
15267     },
15268
15269     /**
15270      * Clears any constraints applied to this instance.  Also clears ticks
15271      * since they can't exist independent of a constraint at this time.
15272      * @method clearConstraints
15273      */
15274     clearConstraints: function() {
15275         this.constrainX = false;
15276         this.constrainY = false;
15277         this.clearTicks();
15278     },
15279
15280     /**
15281      * Clears any tick interval defined for this instance
15282      * @method clearTicks
15283      */
15284     clearTicks: function() {
15285         this.xTicks = null;
15286         this.yTicks = null;
15287         this.xTickSize = 0;
15288         this.yTickSize = 0;
15289     },
15290
15291     /**
15292      * By default, the element can be dragged any place on the screen.  Set
15293      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15294      * parameters if you want to lock the drag to the x axis.
15295      * @method setYConstraint
15296      * @param {int} iUp the number of pixels the element can move up
15297      * @param {int} iDown the number of pixels the element can move down
15298      * @param {int} iTickSize optional parameter for specifying that the
15299      * element should move iTickSize pixels at a time.
15300      */
15301     setYConstraint: function(iUp, iDown, iTickSize) {
15302         this.topConstraint = iUp;
15303         this.bottomConstraint = iDown;
15304
15305         this.minY = this.initPageY - iUp;
15306         this.maxY = this.initPageY + iDown;
15307         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15308
15309         this.constrainY = true;
15310
15311     },
15312
15313     /**
15314      * resetConstraints must be called if you manually reposition a dd element.
15315      * @method resetConstraints
15316      * @param {boolean} maintainOffset
15317      */
15318     resetConstraints: function() {
15319
15320
15321         // Maintain offsets if necessary
15322         if (this.initPageX || this.initPageX === 0) {
15323             // figure out how much this thing has moved
15324             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15325             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15326
15327             this.setInitPosition(dx, dy);
15328
15329         // This is the first time we have detected the element's position
15330         } else {
15331             this.setInitPosition();
15332         }
15333
15334         if (this.constrainX) {
15335             this.setXConstraint( this.leftConstraint,
15336                                  this.rightConstraint,
15337                                  this.xTickSize        );
15338         }
15339
15340         if (this.constrainY) {
15341             this.setYConstraint( this.topConstraint,
15342                                  this.bottomConstraint,
15343                                  this.yTickSize         );
15344         }
15345     },
15346
15347     /**
15348      * Normally the drag element is moved pixel by pixel, but we can specify
15349      * that it move a number of pixels at a time.  This method resolves the
15350      * location when we have it set up like this.
15351      * @method getTick
15352      * @param {int} val where we want to place the object
15353      * @param {int[]} tickArray sorted array of valid points
15354      * @return {int} the closest tick
15355      * @private
15356      */
15357     getTick: function(val, tickArray) {
15358
15359         if (!tickArray) {
15360             // If tick interval is not defined, it is effectively 1 pixel,
15361             // so we return the value passed to us.
15362             return val;
15363         } else if (tickArray[0] >= val) {
15364             // The value is lower than the first tick, so we return the first
15365             // tick.
15366             return tickArray[0];
15367         } else {
15368             for (var i=0, len=tickArray.length; i<len; ++i) {
15369                 var next = i + 1;
15370                 if (tickArray[next] && tickArray[next] >= val) {
15371                     var diff1 = val - tickArray[i];
15372                     var diff2 = tickArray[next] - val;
15373                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15374                 }
15375             }
15376
15377             // The value is larger than the last tick, so we return the last
15378             // tick.
15379             return tickArray[tickArray.length - 1];
15380         }
15381     },
15382
15383     /**
15384      * toString method
15385      * @method toString
15386      * @return {string} string representation of the dd obj
15387      */
15388     toString: function() {
15389         return ("DragDrop " + this.id);
15390     }
15391
15392 };
15393
15394 })();
15395 /*
15396  * Based on:
15397  * Ext JS Library 1.1.1
15398  * Copyright(c) 2006-2007, Ext JS, LLC.
15399  *
15400  * Originally Released Under LGPL - original licence link has changed is not relivant.
15401  *
15402  * Fork - LGPL
15403  * <script type="text/javascript">
15404  */
15405
15406
15407 /**
15408  * The drag and drop utility provides a framework for building drag and drop
15409  * applications.  In addition to enabling drag and drop for specific elements,
15410  * the drag and drop elements are tracked by the manager class, and the
15411  * interactions between the various elements are tracked during the drag and
15412  * the implementing code is notified about these important moments.
15413  */
15414
15415 // Only load the library once.  Rewriting the manager class would orphan
15416 // existing drag and drop instances.
15417 if (!Roo.dd.DragDropMgr) {
15418
15419 /**
15420  * @class Roo.dd.DragDropMgr
15421  * DragDropMgr is a singleton that tracks the element interaction for
15422  * all DragDrop items in the window.  Generally, you will not call
15423  * this class directly, but it does have helper methods that could
15424  * be useful in your DragDrop implementations.
15425  * @singleton
15426  */
15427 Roo.dd.DragDropMgr = function() {
15428
15429     var Event = Roo.EventManager;
15430
15431     return {
15432
15433         /**
15434          * Two dimensional Array of registered DragDrop objects.  The first
15435          * dimension is the DragDrop item group, the second the DragDrop
15436          * object.
15437          * @property ids
15438          * @type {string: string}
15439          * @private
15440          * @static
15441          */
15442         ids: {},
15443
15444         /**
15445          * Array of element ids defined as drag handles.  Used to determine
15446          * if the element that generated the mousedown event is actually the
15447          * handle and not the html element itself.
15448          * @property handleIds
15449          * @type {string: string}
15450          * @private
15451          * @static
15452          */
15453         handleIds: {},
15454
15455         /**
15456          * the DragDrop object that is currently being dragged
15457          * @property dragCurrent
15458          * @type DragDrop
15459          * @private
15460          * @static
15461          **/
15462         dragCurrent: null,
15463
15464         /**
15465          * the DragDrop object(s) that are being hovered over
15466          * @property dragOvers
15467          * @type Array
15468          * @private
15469          * @static
15470          */
15471         dragOvers: {},
15472
15473         /**
15474          * the X distance between the cursor and the object being dragged
15475          * @property deltaX
15476          * @type int
15477          * @private
15478          * @static
15479          */
15480         deltaX: 0,
15481
15482         /**
15483          * the Y distance between the cursor and the object being dragged
15484          * @property deltaY
15485          * @type int
15486          * @private
15487          * @static
15488          */
15489         deltaY: 0,
15490
15491         /**
15492          * Flag to determine if we should prevent the default behavior of the
15493          * events we define. By default this is true, but this can be set to
15494          * false if you need the default behavior (not recommended)
15495          * @property preventDefault
15496          * @type boolean
15497          * @static
15498          */
15499         preventDefault: true,
15500
15501         /**
15502          * Flag to determine if we should stop the propagation of the events
15503          * we generate. This is true by default but you may want to set it to
15504          * false if the html element contains other features that require the
15505          * mouse click.
15506          * @property stopPropagation
15507          * @type boolean
15508          * @static
15509          */
15510         stopPropagation: true,
15511
15512         /**
15513          * Internal flag that is set to true when drag and drop has been
15514          * intialized
15515          * @property initialized
15516          * @private
15517          * @static
15518          */
15519         initalized: false,
15520
15521         /**
15522          * All drag and drop can be disabled.
15523          * @property locked
15524          * @private
15525          * @static
15526          */
15527         locked: false,
15528
15529         /**
15530          * Called the first time an element is registered.
15531          * @method init
15532          * @private
15533          * @static
15534          */
15535         init: function() {
15536             this.initialized = true;
15537         },
15538
15539         /**
15540          * In point mode, drag and drop interaction is defined by the
15541          * location of the cursor during the drag/drop
15542          * @property POINT
15543          * @type int
15544          * @static
15545          */
15546         POINT: 0,
15547
15548         /**
15549          * In intersect mode, drag and drop interactio nis defined by the
15550          * overlap of two or more drag and drop objects.
15551          * @property INTERSECT
15552          * @type int
15553          * @static
15554          */
15555         INTERSECT: 1,
15556
15557         /**
15558          * The current drag and drop mode.  Default: POINT
15559          * @property mode
15560          * @type int
15561          * @static
15562          */
15563         mode: 0,
15564
15565         /**
15566          * Runs method on all drag and drop objects
15567          * @method _execOnAll
15568          * @private
15569          * @static
15570          */
15571         _execOnAll: function(sMethod, args) {
15572             for (var i in this.ids) {
15573                 for (var j in this.ids[i]) {
15574                     var oDD = this.ids[i][j];
15575                     if (! this.isTypeOfDD(oDD)) {
15576                         continue;
15577                     }
15578                     oDD[sMethod].apply(oDD, args);
15579                 }
15580             }
15581         },
15582
15583         /**
15584          * Drag and drop initialization.  Sets up the global event handlers
15585          * @method _onLoad
15586          * @private
15587          * @static
15588          */
15589         _onLoad: function() {
15590
15591             this.init();
15592
15593
15594             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15595             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15596             Event.on(window,   "unload",    this._onUnload, this, true);
15597             Event.on(window,   "resize",    this._onResize, this, true);
15598             // Event.on(window,   "mouseout",    this._test);
15599
15600         },
15601
15602         /**
15603          * Reset constraints on all drag and drop objs
15604          * @method _onResize
15605          * @private
15606          * @static
15607          */
15608         _onResize: function(e) {
15609             this._execOnAll("resetConstraints", []);
15610         },
15611
15612         /**
15613          * Lock all drag and drop functionality
15614          * @method lock
15615          * @static
15616          */
15617         lock: function() { this.locked = true; },
15618
15619         /**
15620          * Unlock all drag and drop functionality
15621          * @method unlock
15622          * @static
15623          */
15624         unlock: function() { this.locked = false; },
15625
15626         /**
15627          * Is drag and drop locked?
15628          * @method isLocked
15629          * @return {boolean} True if drag and drop is locked, false otherwise.
15630          * @static
15631          */
15632         isLocked: function() { return this.locked; },
15633
15634         /**
15635          * Location cache that is set for all drag drop objects when a drag is
15636          * initiated, cleared when the drag is finished.
15637          * @property locationCache
15638          * @private
15639          * @static
15640          */
15641         locationCache: {},
15642
15643         /**
15644          * Set useCache to false if you want to force object the lookup of each
15645          * drag and drop linked element constantly during a drag.
15646          * @property useCache
15647          * @type boolean
15648          * @static
15649          */
15650         useCache: true,
15651
15652         /**
15653          * The number of pixels that the mouse needs to move after the
15654          * mousedown before the drag is initiated.  Default=3;
15655          * @property clickPixelThresh
15656          * @type int
15657          * @static
15658          */
15659         clickPixelThresh: 3,
15660
15661         /**
15662          * The number of milliseconds after the mousedown event to initiate the
15663          * drag if we don't get a mouseup event. Default=1000
15664          * @property clickTimeThresh
15665          * @type int
15666          * @static
15667          */
15668         clickTimeThresh: 350,
15669
15670         /**
15671          * Flag that indicates that either the drag pixel threshold or the
15672          * mousdown time threshold has been met
15673          * @property dragThreshMet
15674          * @type boolean
15675          * @private
15676          * @static
15677          */
15678         dragThreshMet: false,
15679
15680         /**
15681          * Timeout used for the click time threshold
15682          * @property clickTimeout
15683          * @type Object
15684          * @private
15685          * @static
15686          */
15687         clickTimeout: null,
15688
15689         /**
15690          * The X position of the mousedown event stored for later use when a
15691          * drag threshold is met.
15692          * @property startX
15693          * @type int
15694          * @private
15695          * @static
15696          */
15697         startX: 0,
15698
15699         /**
15700          * The Y position of the mousedown event stored for later use when a
15701          * drag threshold is met.
15702          * @property startY
15703          * @type int
15704          * @private
15705          * @static
15706          */
15707         startY: 0,
15708
15709         /**
15710          * Each DragDrop instance must be registered with the DragDropMgr.
15711          * This is executed in DragDrop.init()
15712          * @method regDragDrop
15713          * @param {DragDrop} oDD the DragDrop object to register
15714          * @param {String} sGroup the name of the group this element belongs to
15715          * @static
15716          */
15717         regDragDrop: function(oDD, sGroup) {
15718             if (!this.initialized) { this.init(); }
15719
15720             if (!this.ids[sGroup]) {
15721                 this.ids[sGroup] = {};
15722             }
15723             this.ids[sGroup][oDD.id] = oDD;
15724         },
15725
15726         /**
15727          * Removes the supplied dd instance from the supplied group. Executed
15728          * by DragDrop.removeFromGroup, so don't call this function directly.
15729          * @method removeDDFromGroup
15730          * @private
15731          * @static
15732          */
15733         removeDDFromGroup: function(oDD, sGroup) {
15734             if (!this.ids[sGroup]) {
15735                 this.ids[sGroup] = {};
15736             }
15737
15738             var obj = this.ids[sGroup];
15739             if (obj && obj[oDD.id]) {
15740                 delete obj[oDD.id];
15741             }
15742         },
15743
15744         /**
15745          * Unregisters a drag and drop item.  This is executed in
15746          * DragDrop.unreg, use that method instead of calling this directly.
15747          * @method _remove
15748          * @private
15749          * @static
15750          */
15751         _remove: function(oDD) {
15752             for (var g in oDD.groups) {
15753                 if (g && this.ids[g][oDD.id]) {
15754                     delete this.ids[g][oDD.id];
15755                 }
15756             }
15757             delete this.handleIds[oDD.id];
15758         },
15759
15760         /**
15761          * Each DragDrop handle element must be registered.  This is done
15762          * automatically when executing DragDrop.setHandleElId()
15763          * @method regHandle
15764          * @param {String} sDDId the DragDrop id this element is a handle for
15765          * @param {String} sHandleId the id of the element that is the drag
15766          * handle
15767          * @static
15768          */
15769         regHandle: function(sDDId, sHandleId) {
15770             if (!this.handleIds[sDDId]) {
15771                 this.handleIds[sDDId] = {};
15772             }
15773             this.handleIds[sDDId][sHandleId] = sHandleId;
15774         },
15775
15776         /**
15777          * Utility function to determine if a given element has been
15778          * registered as a drag drop item.
15779          * @method isDragDrop
15780          * @param {String} id the element id to check
15781          * @return {boolean} true if this element is a DragDrop item,
15782          * false otherwise
15783          * @static
15784          */
15785         isDragDrop: function(id) {
15786             return ( this.getDDById(id) ) ? true : false;
15787         },
15788
15789         /**
15790          * Returns the drag and drop instances that are in all groups the
15791          * passed in instance belongs to.
15792          * @method getRelated
15793          * @param {DragDrop} p_oDD the obj to get related data for
15794          * @param {boolean} bTargetsOnly if true, only return targetable objs
15795          * @return {DragDrop[]} the related instances
15796          * @static
15797          */
15798         getRelated: function(p_oDD, bTargetsOnly) {
15799             var oDDs = [];
15800             for (var i in p_oDD.groups) {
15801                 for (j in this.ids[i]) {
15802                     var dd = this.ids[i][j];
15803                     if (! this.isTypeOfDD(dd)) {
15804                         continue;
15805                     }
15806                     if (!bTargetsOnly || dd.isTarget) {
15807                         oDDs[oDDs.length] = dd;
15808                     }
15809                 }
15810             }
15811
15812             return oDDs;
15813         },
15814
15815         /**
15816          * Returns true if the specified dd target is a legal target for
15817          * the specifice drag obj
15818          * @method isLegalTarget
15819          * @param {DragDrop} the drag obj
15820          * @param {DragDrop} the target
15821          * @return {boolean} true if the target is a legal target for the
15822          * dd obj
15823          * @static
15824          */
15825         isLegalTarget: function (oDD, oTargetDD) {
15826             var targets = this.getRelated(oDD, true);
15827             for (var i=0, len=targets.length;i<len;++i) {
15828                 if (targets[i].id == oTargetDD.id) {
15829                     return true;
15830                 }
15831             }
15832
15833             return false;
15834         },
15835
15836         /**
15837          * My goal is to be able to transparently determine if an object is
15838          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15839          * returns "object", oDD.constructor.toString() always returns
15840          * "DragDrop" and not the name of the subclass.  So for now it just
15841          * evaluates a well-known variable in DragDrop.
15842          * @method isTypeOfDD
15843          * @param {Object} the object to evaluate
15844          * @return {boolean} true if typeof oDD = DragDrop
15845          * @static
15846          */
15847         isTypeOfDD: function (oDD) {
15848             return (oDD && oDD.__ygDragDrop);
15849         },
15850
15851         /**
15852          * Utility function to determine if a given element has been
15853          * registered as a drag drop handle for the given Drag Drop object.
15854          * @method isHandle
15855          * @param {String} id the element id to check
15856          * @return {boolean} true if this element is a DragDrop handle, false
15857          * otherwise
15858          * @static
15859          */
15860         isHandle: function(sDDId, sHandleId) {
15861             return ( this.handleIds[sDDId] &&
15862                             this.handleIds[sDDId][sHandleId] );
15863         },
15864
15865         /**
15866          * Returns the DragDrop instance for a given id
15867          * @method getDDById
15868          * @param {String} id the id of the DragDrop object
15869          * @return {DragDrop} the drag drop object, null if it is not found
15870          * @static
15871          */
15872         getDDById: function(id) {
15873             for (var i in this.ids) {
15874                 if (this.ids[i][id]) {
15875                     return this.ids[i][id];
15876                 }
15877             }
15878             return null;
15879         },
15880
15881         /**
15882          * Fired after a registered DragDrop object gets the mousedown event.
15883          * Sets up the events required to track the object being dragged
15884          * @method handleMouseDown
15885          * @param {Event} e the event
15886          * @param oDD the DragDrop object being dragged
15887          * @private
15888          * @static
15889          */
15890         handleMouseDown: function(e, oDD) {
15891             if(Roo.QuickTips){
15892                 Roo.QuickTips.disable();
15893             }
15894             this.currentTarget = e.getTarget();
15895
15896             this.dragCurrent = oDD;
15897
15898             var el = oDD.getEl();
15899
15900             // track start position
15901             this.startX = e.getPageX();
15902             this.startY = e.getPageY();
15903
15904             this.deltaX = this.startX - el.offsetLeft;
15905             this.deltaY = this.startY - el.offsetTop;
15906
15907             this.dragThreshMet = false;
15908
15909             this.clickTimeout = setTimeout(
15910                     function() {
15911                         var DDM = Roo.dd.DDM;
15912                         DDM.startDrag(DDM.startX, DDM.startY);
15913                     },
15914                     this.clickTimeThresh );
15915         },
15916
15917         /**
15918          * Fired when either the drag pixel threshol or the mousedown hold
15919          * time threshold has been met.
15920          * @method startDrag
15921          * @param x {int} the X position of the original mousedown
15922          * @param y {int} the Y position of the original mousedown
15923          * @static
15924          */
15925         startDrag: function(x, y) {
15926             clearTimeout(this.clickTimeout);
15927             if (this.dragCurrent) {
15928                 this.dragCurrent.b4StartDrag(x, y);
15929                 this.dragCurrent.startDrag(x, y);
15930             }
15931             this.dragThreshMet = true;
15932         },
15933
15934         /**
15935          * Internal function to handle the mouseup event.  Will be invoked
15936          * from the context of the document.
15937          * @method handleMouseUp
15938          * @param {Event} e the event
15939          * @private
15940          * @static
15941          */
15942         handleMouseUp: function(e) {
15943
15944             if(Roo.QuickTips){
15945                 Roo.QuickTips.enable();
15946             }
15947             if (! this.dragCurrent) {
15948                 return;
15949             }
15950
15951             clearTimeout(this.clickTimeout);
15952
15953             if (this.dragThreshMet) {
15954                 this.fireEvents(e, true);
15955             } else {
15956             }
15957
15958             this.stopDrag(e);
15959
15960             this.stopEvent(e);
15961         },
15962
15963         /**
15964          * Utility to stop event propagation and event default, if these
15965          * features are turned on.
15966          * @method stopEvent
15967          * @param {Event} e the event as returned by this.getEvent()
15968          * @static
15969          */
15970         stopEvent: function(e){
15971             if(this.stopPropagation) {
15972                 e.stopPropagation();
15973             }
15974
15975             if (this.preventDefault) {
15976                 e.preventDefault();
15977             }
15978         },
15979
15980         /**
15981          * Internal function to clean up event handlers after the drag
15982          * operation is complete
15983          * @method stopDrag
15984          * @param {Event} e the event
15985          * @private
15986          * @static
15987          */
15988         stopDrag: function(e) {
15989             // Fire the drag end event for the item that was dragged
15990             if (this.dragCurrent) {
15991                 if (this.dragThreshMet) {
15992                     this.dragCurrent.b4EndDrag(e);
15993                     this.dragCurrent.endDrag(e);
15994                 }
15995
15996                 this.dragCurrent.onMouseUp(e);
15997             }
15998
15999             this.dragCurrent = null;
16000             this.dragOvers = {};
16001         },
16002
16003         /**
16004          * Internal function to handle the mousemove event.  Will be invoked
16005          * from the context of the html element.
16006          *
16007          * @TODO figure out what we can do about mouse events lost when the
16008          * user drags objects beyond the window boundary.  Currently we can
16009          * detect this in internet explorer by verifying that the mouse is
16010          * down during the mousemove event.  Firefox doesn't give us the
16011          * button state on the mousemove event.
16012          * @method handleMouseMove
16013          * @param {Event} e the event
16014          * @private
16015          * @static
16016          */
16017         handleMouseMove: function(e) {
16018             if (! this.dragCurrent) {
16019                 return true;
16020             }
16021
16022             // var button = e.which || e.button;
16023
16024             // check for IE mouseup outside of page boundary
16025             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16026                 this.stopEvent(e);
16027                 return this.handleMouseUp(e);
16028             }
16029
16030             if (!this.dragThreshMet) {
16031                 var diffX = Math.abs(this.startX - e.getPageX());
16032                 var diffY = Math.abs(this.startY - e.getPageY());
16033                 if (diffX > this.clickPixelThresh ||
16034                             diffY > this.clickPixelThresh) {
16035                     this.startDrag(this.startX, this.startY);
16036                 }
16037             }
16038
16039             if (this.dragThreshMet) {
16040                 this.dragCurrent.b4Drag(e);
16041                 this.dragCurrent.onDrag(e);
16042                 if(!this.dragCurrent.moveOnly){
16043                     this.fireEvents(e, false);
16044                 }
16045             }
16046
16047             this.stopEvent(e);
16048
16049             return true;
16050         },
16051
16052         /**
16053          * Iterates over all of the DragDrop elements to find ones we are
16054          * hovering over or dropping on
16055          * @method fireEvents
16056          * @param {Event} e the event
16057          * @param {boolean} isDrop is this a drop op or a mouseover op?
16058          * @private
16059          * @static
16060          */
16061         fireEvents: function(e, isDrop) {
16062             var dc = this.dragCurrent;
16063
16064             // If the user did the mouse up outside of the window, we could
16065             // get here even though we have ended the drag.
16066             if (!dc || dc.isLocked()) {
16067                 return;
16068             }
16069
16070             var pt = e.getPoint();
16071
16072             // cache the previous dragOver array
16073             var oldOvers = [];
16074
16075             var outEvts   = [];
16076             var overEvts  = [];
16077             var dropEvts  = [];
16078             var enterEvts = [];
16079
16080             // Check to see if the object(s) we were hovering over is no longer
16081             // being hovered over so we can fire the onDragOut event
16082             for (var i in this.dragOvers) {
16083
16084                 var ddo = this.dragOvers[i];
16085
16086                 if (! this.isTypeOfDD(ddo)) {
16087                     continue;
16088                 }
16089
16090                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16091                     outEvts.push( ddo );
16092                 }
16093
16094                 oldOvers[i] = true;
16095                 delete this.dragOvers[i];
16096             }
16097
16098             for (var sGroup in dc.groups) {
16099
16100                 if ("string" != typeof sGroup) {
16101                     continue;
16102                 }
16103
16104                 for (i in this.ids[sGroup]) {
16105                     var oDD = this.ids[sGroup][i];
16106                     if (! this.isTypeOfDD(oDD)) {
16107                         continue;
16108                     }
16109
16110                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16111                         if (this.isOverTarget(pt, oDD, this.mode)) {
16112                             // look for drop interactions
16113                             if (isDrop) {
16114                                 dropEvts.push( oDD );
16115                             // look for drag enter and drag over interactions
16116                             } else {
16117
16118                                 // initial drag over: dragEnter fires
16119                                 if (!oldOvers[oDD.id]) {
16120                                     enterEvts.push( oDD );
16121                                 // subsequent drag overs: dragOver fires
16122                                 } else {
16123                                     overEvts.push( oDD );
16124                                 }
16125
16126                                 this.dragOvers[oDD.id] = oDD;
16127                             }
16128                         }
16129                     }
16130                 }
16131             }
16132
16133             if (this.mode) {
16134                 if (outEvts.length) {
16135                     dc.b4DragOut(e, outEvts);
16136                     dc.onDragOut(e, outEvts);
16137                 }
16138
16139                 if (enterEvts.length) {
16140                     dc.onDragEnter(e, enterEvts);
16141                 }
16142
16143                 if (overEvts.length) {
16144                     dc.b4DragOver(e, overEvts);
16145                     dc.onDragOver(e, overEvts);
16146                 }
16147
16148                 if (dropEvts.length) {
16149                     dc.b4DragDrop(e, dropEvts);
16150                     dc.onDragDrop(e, dropEvts);
16151                 }
16152
16153             } else {
16154                 // fire dragout events
16155                 var len = 0;
16156                 for (i=0, len=outEvts.length; i<len; ++i) {
16157                     dc.b4DragOut(e, outEvts[i].id);
16158                     dc.onDragOut(e, outEvts[i].id);
16159                 }
16160
16161                 // fire enter events
16162                 for (i=0,len=enterEvts.length; i<len; ++i) {
16163                     // dc.b4DragEnter(e, oDD.id);
16164                     dc.onDragEnter(e, enterEvts[i].id);
16165                 }
16166
16167                 // fire over events
16168                 for (i=0,len=overEvts.length; i<len; ++i) {
16169                     dc.b4DragOver(e, overEvts[i].id);
16170                     dc.onDragOver(e, overEvts[i].id);
16171                 }
16172
16173                 // fire drop events
16174                 for (i=0, len=dropEvts.length; i<len; ++i) {
16175                     dc.b4DragDrop(e, dropEvts[i].id);
16176                     dc.onDragDrop(e, dropEvts[i].id);
16177                 }
16178
16179             }
16180
16181             // notify about a drop that did not find a target
16182             if (isDrop && !dropEvts.length) {
16183                 dc.onInvalidDrop(e);
16184             }
16185
16186         },
16187
16188         /**
16189          * Helper function for getting the best match from the list of drag
16190          * and drop objects returned by the drag and drop events when we are
16191          * in INTERSECT mode.  It returns either the first object that the
16192          * cursor is over, or the object that has the greatest overlap with
16193          * the dragged element.
16194          * @method getBestMatch
16195          * @param  {DragDrop[]} dds The array of drag and drop objects
16196          * targeted
16197          * @return {DragDrop}       The best single match
16198          * @static
16199          */
16200         getBestMatch: function(dds) {
16201             var winner = null;
16202             // Return null if the input is not what we expect
16203             //if (!dds || !dds.length || dds.length == 0) {
16204                // winner = null;
16205             // If there is only one item, it wins
16206             //} else if (dds.length == 1) {
16207
16208             var len = dds.length;
16209
16210             if (len == 1) {
16211                 winner = dds[0];
16212             } else {
16213                 // Loop through the targeted items
16214                 for (var i=0; i<len; ++i) {
16215                     var dd = dds[i];
16216                     // If the cursor is over the object, it wins.  If the
16217                     // cursor is over multiple matches, the first one we come
16218                     // to wins.
16219                     if (dd.cursorIsOver) {
16220                         winner = dd;
16221                         break;
16222                     // Otherwise the object with the most overlap wins
16223                     } else {
16224                         if (!winner ||
16225                             winner.overlap.getArea() < dd.overlap.getArea()) {
16226                             winner = dd;
16227                         }
16228                     }
16229                 }
16230             }
16231
16232             return winner;
16233         },
16234
16235         /**
16236          * Refreshes the cache of the top-left and bottom-right points of the
16237          * drag and drop objects in the specified group(s).  This is in the
16238          * format that is stored in the drag and drop instance, so typical
16239          * usage is:
16240          * <code>
16241          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16242          * </code>
16243          * Alternatively:
16244          * <code>
16245          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16246          * </code>
16247          * @TODO this really should be an indexed array.  Alternatively this
16248          * method could accept both.
16249          * @method refreshCache
16250          * @param {Object} groups an associative array of groups to refresh
16251          * @static
16252          */
16253         refreshCache: function(groups) {
16254             for (var sGroup in groups) {
16255                 if ("string" != typeof sGroup) {
16256                     continue;
16257                 }
16258                 for (var i in this.ids[sGroup]) {
16259                     var oDD = this.ids[sGroup][i];
16260
16261                     if (this.isTypeOfDD(oDD)) {
16262                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16263                         var loc = this.getLocation(oDD);
16264                         if (loc) {
16265                             this.locationCache[oDD.id] = loc;
16266                         } else {
16267                             delete this.locationCache[oDD.id];
16268                             // this will unregister the drag and drop object if
16269                             // the element is not in a usable state
16270                             // oDD.unreg();
16271                         }
16272                     }
16273                 }
16274             }
16275         },
16276
16277         /**
16278          * This checks to make sure an element exists and is in the DOM.  The
16279          * main purpose is to handle cases where innerHTML is used to remove
16280          * drag and drop objects from the DOM.  IE provides an 'unspecified
16281          * error' when trying to access the offsetParent of such an element
16282          * @method verifyEl
16283          * @param {HTMLElement} el the element to check
16284          * @return {boolean} true if the element looks usable
16285          * @static
16286          */
16287         verifyEl: function(el) {
16288             if (el) {
16289                 var parent;
16290                 if(Roo.isIE){
16291                     try{
16292                         parent = el.offsetParent;
16293                     }catch(e){}
16294                 }else{
16295                     parent = el.offsetParent;
16296                 }
16297                 if (parent) {
16298                     return true;
16299                 }
16300             }
16301
16302             return false;
16303         },
16304
16305         /**
16306          * Returns a Region object containing the drag and drop element's position
16307          * and size, including the padding configured for it
16308          * @method getLocation
16309          * @param {DragDrop} oDD the drag and drop object to get the
16310          *                       location for
16311          * @return {Roo.lib.Region} a Region object representing the total area
16312          *                             the element occupies, including any padding
16313          *                             the instance is configured for.
16314          * @static
16315          */
16316         getLocation: function(oDD) {
16317             if (! this.isTypeOfDD(oDD)) {
16318                 return null;
16319             }
16320
16321             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16322
16323             try {
16324                 pos= Roo.lib.Dom.getXY(el);
16325             } catch (e) { }
16326
16327             if (!pos) {
16328                 return null;
16329             }
16330
16331             x1 = pos[0];
16332             x2 = x1 + el.offsetWidth;
16333             y1 = pos[1];
16334             y2 = y1 + el.offsetHeight;
16335
16336             t = y1 - oDD.padding[0];
16337             r = x2 + oDD.padding[1];
16338             b = y2 + oDD.padding[2];
16339             l = x1 - oDD.padding[3];
16340
16341             return new Roo.lib.Region( t, r, b, l );
16342         },
16343
16344         /**
16345          * Checks the cursor location to see if it over the target
16346          * @method isOverTarget
16347          * @param {Roo.lib.Point} pt The point to evaluate
16348          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16349          * @return {boolean} true if the mouse is over the target
16350          * @private
16351          * @static
16352          */
16353         isOverTarget: function(pt, oTarget, intersect) {
16354             // use cache if available
16355             var loc = this.locationCache[oTarget.id];
16356             if (!loc || !this.useCache) {
16357                 loc = this.getLocation(oTarget);
16358                 this.locationCache[oTarget.id] = loc;
16359
16360             }
16361
16362             if (!loc) {
16363                 return false;
16364             }
16365
16366             oTarget.cursorIsOver = loc.contains( pt );
16367
16368             // DragDrop is using this as a sanity check for the initial mousedown
16369             // in this case we are done.  In POINT mode, if the drag obj has no
16370             // contraints, we are also done. Otherwise we need to evaluate the
16371             // location of the target as related to the actual location of the
16372             // dragged element.
16373             var dc = this.dragCurrent;
16374             if (!dc || !dc.getTargetCoord ||
16375                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16376                 return oTarget.cursorIsOver;
16377             }
16378
16379             oTarget.overlap = null;
16380
16381             // Get the current location of the drag element, this is the
16382             // location of the mouse event less the delta that represents
16383             // where the original mousedown happened on the element.  We
16384             // need to consider constraints and ticks as well.
16385             var pos = dc.getTargetCoord(pt.x, pt.y);
16386
16387             var el = dc.getDragEl();
16388             var curRegion = new Roo.lib.Region( pos.y,
16389                                                    pos.x + el.offsetWidth,
16390                                                    pos.y + el.offsetHeight,
16391                                                    pos.x );
16392
16393             var overlap = curRegion.intersect(loc);
16394
16395             if (overlap) {
16396                 oTarget.overlap = overlap;
16397                 return (intersect) ? true : oTarget.cursorIsOver;
16398             } else {
16399                 return false;
16400             }
16401         },
16402
16403         /**
16404          * unload event handler
16405          * @method _onUnload
16406          * @private
16407          * @static
16408          */
16409         _onUnload: function(e, me) {
16410             Roo.dd.DragDropMgr.unregAll();
16411         },
16412
16413         /**
16414          * Cleans up the drag and drop events and objects.
16415          * @method unregAll
16416          * @private
16417          * @static
16418          */
16419         unregAll: function() {
16420
16421             if (this.dragCurrent) {
16422                 this.stopDrag();
16423                 this.dragCurrent = null;
16424             }
16425
16426             this._execOnAll("unreg", []);
16427
16428             for (i in this.elementCache) {
16429                 delete this.elementCache[i];
16430             }
16431
16432             this.elementCache = {};
16433             this.ids = {};
16434         },
16435
16436         /**
16437          * A cache of DOM elements
16438          * @property elementCache
16439          * @private
16440          * @static
16441          */
16442         elementCache: {},
16443
16444         /**
16445          * Get the wrapper for the DOM element specified
16446          * @method getElWrapper
16447          * @param {String} id the id of the element to get
16448          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16449          * @private
16450          * @deprecated This wrapper isn't that useful
16451          * @static
16452          */
16453         getElWrapper: function(id) {
16454             var oWrapper = this.elementCache[id];
16455             if (!oWrapper || !oWrapper.el) {
16456                 oWrapper = this.elementCache[id] =
16457                     new this.ElementWrapper(Roo.getDom(id));
16458             }
16459             return oWrapper;
16460         },
16461
16462         /**
16463          * Returns the actual DOM element
16464          * @method getElement
16465          * @param {String} id the id of the elment to get
16466          * @return {Object} The element
16467          * @deprecated use Roo.getDom instead
16468          * @static
16469          */
16470         getElement: function(id) {
16471             return Roo.getDom(id);
16472         },
16473
16474         /**
16475          * Returns the style property for the DOM element (i.e.,
16476          * document.getElById(id).style)
16477          * @method getCss
16478          * @param {String} id the id of the elment to get
16479          * @return {Object} The style property of the element
16480          * @deprecated use Roo.getDom instead
16481          * @static
16482          */
16483         getCss: function(id) {
16484             var el = Roo.getDom(id);
16485             return (el) ? el.style : null;
16486         },
16487
16488         /**
16489          * Inner class for cached elements
16490          * @class DragDropMgr.ElementWrapper
16491          * @for DragDropMgr
16492          * @private
16493          * @deprecated
16494          */
16495         ElementWrapper: function(el) {
16496                 /**
16497                  * The element
16498                  * @property el
16499                  */
16500                 this.el = el || null;
16501                 /**
16502                  * The element id
16503                  * @property id
16504                  */
16505                 this.id = this.el && el.id;
16506                 /**
16507                  * A reference to the style property
16508                  * @property css
16509                  */
16510                 this.css = this.el && el.style;
16511             },
16512
16513         /**
16514          * Returns the X position of an html element
16515          * @method getPosX
16516          * @param el the element for which to get the position
16517          * @return {int} the X coordinate
16518          * @for DragDropMgr
16519          * @deprecated use Roo.lib.Dom.getX instead
16520          * @static
16521          */
16522         getPosX: function(el) {
16523             return Roo.lib.Dom.getX(el);
16524         },
16525
16526         /**
16527          * Returns the Y position of an html element
16528          * @method getPosY
16529          * @param el the element for which to get the position
16530          * @return {int} the Y coordinate
16531          * @deprecated use Roo.lib.Dom.getY instead
16532          * @static
16533          */
16534         getPosY: function(el) {
16535             return Roo.lib.Dom.getY(el);
16536         },
16537
16538         /**
16539          * Swap two nodes.  In IE, we use the native method, for others we
16540          * emulate the IE behavior
16541          * @method swapNode
16542          * @param n1 the first node to swap
16543          * @param n2 the other node to swap
16544          * @static
16545          */
16546         swapNode: function(n1, n2) {
16547             if (n1.swapNode) {
16548                 n1.swapNode(n2);
16549             } else {
16550                 var p = n2.parentNode;
16551                 var s = n2.nextSibling;
16552
16553                 if (s == n1) {
16554                     p.insertBefore(n1, n2);
16555                 } else if (n2 == n1.nextSibling) {
16556                     p.insertBefore(n2, n1);
16557                 } else {
16558                     n1.parentNode.replaceChild(n2, n1);
16559                     p.insertBefore(n1, s);
16560                 }
16561             }
16562         },
16563
16564         /**
16565          * Returns the current scroll position
16566          * @method getScroll
16567          * @private
16568          * @static
16569          */
16570         getScroll: function () {
16571             var t, l, dde=document.documentElement, db=document.body;
16572             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16573                 t = dde.scrollTop;
16574                 l = dde.scrollLeft;
16575             } else if (db) {
16576                 t = db.scrollTop;
16577                 l = db.scrollLeft;
16578             } else {
16579
16580             }
16581             return { top: t, left: l };
16582         },
16583
16584         /**
16585          * Returns the specified element style property
16586          * @method getStyle
16587          * @param {HTMLElement} el          the element
16588          * @param {string}      styleProp   the style property
16589          * @return {string} The value of the style property
16590          * @deprecated use Roo.lib.Dom.getStyle
16591          * @static
16592          */
16593         getStyle: function(el, styleProp) {
16594             return Roo.fly(el).getStyle(styleProp);
16595         },
16596
16597         /**
16598          * Gets the scrollTop
16599          * @method getScrollTop
16600          * @return {int} the document's scrollTop
16601          * @static
16602          */
16603         getScrollTop: function () { return this.getScroll().top; },
16604
16605         /**
16606          * Gets the scrollLeft
16607          * @method getScrollLeft
16608          * @return {int} the document's scrollTop
16609          * @static
16610          */
16611         getScrollLeft: function () { return this.getScroll().left; },
16612
16613         /**
16614          * Sets the x/y position of an element to the location of the
16615          * target element.
16616          * @method moveToEl
16617          * @param {HTMLElement} moveEl      The element to move
16618          * @param {HTMLElement} targetEl    The position reference element
16619          * @static
16620          */
16621         moveToEl: function (moveEl, targetEl) {
16622             var aCoord = Roo.lib.Dom.getXY(targetEl);
16623             Roo.lib.Dom.setXY(moveEl, aCoord);
16624         },
16625
16626         /**
16627          * Numeric array sort function
16628          * @method numericSort
16629          * @static
16630          */
16631         numericSort: function(a, b) { return (a - b); },
16632
16633         /**
16634          * Internal counter
16635          * @property _timeoutCount
16636          * @private
16637          * @static
16638          */
16639         _timeoutCount: 0,
16640
16641         /**
16642          * Trying to make the load order less important.  Without this we get
16643          * an error if this file is loaded before the Event Utility.
16644          * @method _addListeners
16645          * @private
16646          * @static
16647          */
16648         _addListeners: function() {
16649             var DDM = Roo.dd.DDM;
16650             if ( Roo.lib.Event && document ) {
16651                 DDM._onLoad();
16652             } else {
16653                 if (DDM._timeoutCount > 2000) {
16654                 } else {
16655                     setTimeout(DDM._addListeners, 10);
16656                     if (document && document.body) {
16657                         DDM._timeoutCount += 1;
16658                     }
16659                 }
16660             }
16661         },
16662
16663         /**
16664          * Recursively searches the immediate parent and all child nodes for
16665          * the handle element in order to determine wheter or not it was
16666          * clicked.
16667          * @method handleWasClicked
16668          * @param node the html element to inspect
16669          * @static
16670          */
16671         handleWasClicked: function(node, id) {
16672             if (this.isHandle(id, node.id)) {
16673                 return true;
16674             } else {
16675                 // check to see if this is a text node child of the one we want
16676                 var p = node.parentNode;
16677
16678                 while (p) {
16679                     if (this.isHandle(id, p.id)) {
16680                         return true;
16681                     } else {
16682                         p = p.parentNode;
16683                     }
16684                 }
16685             }
16686
16687             return false;
16688         }
16689
16690     };
16691
16692 }();
16693
16694 // shorter alias, save a few bytes
16695 Roo.dd.DDM = Roo.dd.DragDropMgr;
16696 Roo.dd.DDM._addListeners();
16697
16698 }/*
16699  * Based on:
16700  * Ext JS Library 1.1.1
16701  * Copyright(c) 2006-2007, Ext JS, LLC.
16702  *
16703  * Originally Released Under LGPL - original licence link has changed is not relivant.
16704  *
16705  * Fork - LGPL
16706  * <script type="text/javascript">
16707  */
16708
16709 /**
16710  * @class Roo.dd.DD
16711  * A DragDrop implementation where the linked element follows the
16712  * mouse cursor during a drag.
16713  * @extends Roo.dd.DragDrop
16714  * @constructor
16715  * @param {String} id the id of the linked element
16716  * @param {String} sGroup the group of related DragDrop items
16717  * @param {object} config an object containing configurable attributes
16718  *                Valid properties for DD:
16719  *                    scroll
16720  */
16721 Roo.dd.DD = function(id, sGroup, config) {
16722     if (id) {
16723         this.init(id, sGroup, config);
16724     }
16725 };
16726
16727 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16728
16729     /**
16730      * When set to true, the utility automatically tries to scroll the browser
16731      * window wehn a drag and drop element is dragged near the viewport boundary.
16732      * Defaults to true.
16733      * @property scroll
16734      * @type boolean
16735      */
16736     scroll: true,
16737
16738     /**
16739      * Sets the pointer offset to the distance between the linked element's top
16740      * left corner and the location the element was clicked
16741      * @method autoOffset
16742      * @param {int} iPageX the X coordinate of the click
16743      * @param {int} iPageY the Y coordinate of the click
16744      */
16745     autoOffset: function(iPageX, iPageY) {
16746         var x = iPageX - this.startPageX;
16747         var y = iPageY - this.startPageY;
16748         this.setDelta(x, y);
16749     },
16750
16751     /**
16752      * Sets the pointer offset.  You can call this directly to force the
16753      * offset to be in a particular location (e.g., pass in 0,0 to set it
16754      * to the center of the object)
16755      * @method setDelta
16756      * @param {int} iDeltaX the distance from the left
16757      * @param {int} iDeltaY the distance from the top
16758      */
16759     setDelta: function(iDeltaX, iDeltaY) {
16760         this.deltaX = iDeltaX;
16761         this.deltaY = iDeltaY;
16762     },
16763
16764     /**
16765      * Sets the drag element to the location of the mousedown or click event,
16766      * maintaining the cursor location relative to the location on the element
16767      * that was clicked.  Override this if you want to place the element in a
16768      * location other than where the cursor is.
16769      * @method setDragElPos
16770      * @param {int} iPageX the X coordinate of the mousedown or drag event
16771      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16772      */
16773     setDragElPos: function(iPageX, iPageY) {
16774         // the first time we do this, we are going to check to make sure
16775         // the element has css positioning
16776
16777         var el = this.getDragEl();
16778         this.alignElWithMouse(el, iPageX, iPageY);
16779     },
16780
16781     /**
16782      * Sets the element to the location of the mousedown or click event,
16783      * maintaining the cursor location relative to the location on the element
16784      * that was clicked.  Override this if you want to place the element in a
16785      * location other than where the cursor is.
16786      * @method alignElWithMouse
16787      * @param {HTMLElement} el the element to move
16788      * @param {int} iPageX the X coordinate of the mousedown or drag event
16789      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16790      */
16791     alignElWithMouse: function(el, iPageX, iPageY) {
16792         var oCoord = this.getTargetCoord(iPageX, iPageY);
16793         var fly = el.dom ? el : Roo.fly(el);
16794         if (!this.deltaSetXY) {
16795             var aCoord = [oCoord.x, oCoord.y];
16796             fly.setXY(aCoord);
16797             var newLeft = fly.getLeft(true);
16798             var newTop  = fly.getTop(true);
16799             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16800         } else {
16801             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16802         }
16803
16804         this.cachePosition(oCoord.x, oCoord.y);
16805         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16806         return oCoord;
16807     },
16808
16809     /**
16810      * Saves the most recent position so that we can reset the constraints and
16811      * tick marks on-demand.  We need to know this so that we can calculate the
16812      * number of pixels the element is offset from its original position.
16813      * @method cachePosition
16814      * @param iPageX the current x position (optional, this just makes it so we
16815      * don't have to look it up again)
16816      * @param iPageY the current y position (optional, this just makes it so we
16817      * don't have to look it up again)
16818      */
16819     cachePosition: function(iPageX, iPageY) {
16820         if (iPageX) {
16821             this.lastPageX = iPageX;
16822             this.lastPageY = iPageY;
16823         } else {
16824             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16825             this.lastPageX = aCoord[0];
16826             this.lastPageY = aCoord[1];
16827         }
16828     },
16829
16830     /**
16831      * Auto-scroll the window if the dragged object has been moved beyond the
16832      * visible window boundary.
16833      * @method autoScroll
16834      * @param {int} x the drag element's x position
16835      * @param {int} y the drag element's y position
16836      * @param {int} h the height of the drag element
16837      * @param {int} w the width of the drag element
16838      * @private
16839      */
16840     autoScroll: function(x, y, h, w) {
16841
16842         if (this.scroll) {
16843             // The client height
16844             var clientH = Roo.lib.Dom.getViewWidth();
16845
16846             // The client width
16847             var clientW = Roo.lib.Dom.getViewHeight();
16848
16849             // The amt scrolled down
16850             var st = this.DDM.getScrollTop();
16851
16852             // The amt scrolled right
16853             var sl = this.DDM.getScrollLeft();
16854
16855             // Location of the bottom of the element
16856             var bot = h + y;
16857
16858             // Location of the right of the element
16859             var right = w + x;
16860
16861             // The distance from the cursor to the bottom of the visible area,
16862             // adjusted so that we don't scroll if the cursor is beyond the
16863             // element drag constraints
16864             var toBot = (clientH + st - y - this.deltaY);
16865
16866             // The distance from the cursor to the right of the visible area
16867             var toRight = (clientW + sl - x - this.deltaX);
16868
16869
16870             // How close to the edge the cursor must be before we scroll
16871             // var thresh = (document.all) ? 100 : 40;
16872             var thresh = 40;
16873
16874             // How many pixels to scroll per autoscroll op.  This helps to reduce
16875             // clunky scrolling. IE is more sensitive about this ... it needs this
16876             // value to be higher.
16877             var scrAmt = (document.all) ? 80 : 30;
16878
16879             // Scroll down if we are near the bottom of the visible page and the
16880             // obj extends below the crease
16881             if ( bot > clientH && toBot < thresh ) {
16882                 window.scrollTo(sl, st + scrAmt);
16883             }
16884
16885             // Scroll up if the window is scrolled down and the top of the object
16886             // goes above the top border
16887             if ( y < st && st > 0 && y - st < thresh ) {
16888                 window.scrollTo(sl, st - scrAmt);
16889             }
16890
16891             // Scroll right if the obj is beyond the right border and the cursor is
16892             // near the border.
16893             if ( right > clientW && toRight < thresh ) {
16894                 window.scrollTo(sl + scrAmt, st);
16895             }
16896
16897             // Scroll left if the window has been scrolled to the right and the obj
16898             // extends past the left border
16899             if ( x < sl && sl > 0 && x - sl < thresh ) {
16900                 window.scrollTo(sl - scrAmt, st);
16901             }
16902         }
16903     },
16904
16905     /**
16906      * Finds the location the element should be placed if we want to move
16907      * it to where the mouse location less the click offset would place us.
16908      * @method getTargetCoord
16909      * @param {int} iPageX the X coordinate of the click
16910      * @param {int} iPageY the Y coordinate of the click
16911      * @return an object that contains the coordinates (Object.x and Object.y)
16912      * @private
16913      */
16914     getTargetCoord: function(iPageX, iPageY) {
16915
16916
16917         var x = iPageX - this.deltaX;
16918         var y = iPageY - this.deltaY;
16919
16920         if (this.constrainX) {
16921             if (x < this.minX) { x = this.minX; }
16922             if (x > this.maxX) { x = this.maxX; }
16923         }
16924
16925         if (this.constrainY) {
16926             if (y < this.minY) { y = this.minY; }
16927             if (y > this.maxY) { y = this.maxY; }
16928         }
16929
16930         x = this.getTick(x, this.xTicks);
16931         y = this.getTick(y, this.yTicks);
16932
16933
16934         return {x:x, y:y};
16935     },
16936
16937     /*
16938      * Sets up config options specific to this class. Overrides
16939      * Roo.dd.DragDrop, but all versions of this method through the
16940      * inheritance chain are called
16941      */
16942     applyConfig: function() {
16943         Roo.dd.DD.superclass.applyConfig.call(this);
16944         this.scroll = (this.config.scroll !== false);
16945     },
16946
16947     /*
16948      * Event that fires prior to the onMouseDown event.  Overrides
16949      * Roo.dd.DragDrop.
16950      */
16951     b4MouseDown: function(e) {
16952         // this.resetConstraints();
16953         this.autoOffset(e.getPageX(),
16954                             e.getPageY());
16955     },
16956
16957     /*
16958      * Event that fires prior to the onDrag event.  Overrides
16959      * Roo.dd.DragDrop.
16960      */
16961     b4Drag: function(e) {
16962         this.setDragElPos(e.getPageX(),
16963                             e.getPageY());
16964     },
16965
16966     toString: function() {
16967         return ("DD " + this.id);
16968     }
16969
16970     //////////////////////////////////////////////////////////////////////////
16971     // Debugging ygDragDrop events that can be overridden
16972     //////////////////////////////////////////////////////////////////////////
16973     /*
16974     startDrag: function(x, y) {
16975     },
16976
16977     onDrag: function(e) {
16978     },
16979
16980     onDragEnter: function(e, id) {
16981     },
16982
16983     onDragOver: function(e, id) {
16984     },
16985
16986     onDragOut: function(e, id) {
16987     },
16988
16989     onDragDrop: function(e, id) {
16990     },
16991
16992     endDrag: function(e) {
16993     }
16994
16995     */
16996
16997 });/*
16998  * Based on:
16999  * Ext JS Library 1.1.1
17000  * Copyright(c) 2006-2007, Ext JS, LLC.
17001  *
17002  * Originally Released Under LGPL - original licence link has changed is not relivant.
17003  *
17004  * Fork - LGPL
17005  * <script type="text/javascript">
17006  */
17007
17008 /**
17009  * @class Roo.dd.DDProxy
17010  * A DragDrop implementation that inserts an empty, bordered div into
17011  * the document that follows the cursor during drag operations.  At the time of
17012  * the click, the frame div is resized to the dimensions of the linked html
17013  * element, and moved to the exact location of the linked element.
17014  *
17015  * References to the "frame" element refer to the single proxy element that
17016  * was created to be dragged in place of all DDProxy elements on the
17017  * page.
17018  *
17019  * @extends Roo.dd.DD
17020  * @constructor
17021  * @param {String} id the id of the linked html element
17022  * @param {String} sGroup the group of related DragDrop objects
17023  * @param {object} config an object containing configurable attributes
17024  *                Valid properties for DDProxy in addition to those in DragDrop:
17025  *                   resizeFrame, centerFrame, dragElId
17026  */
17027 Roo.dd.DDProxy = function(id, sGroup, config) {
17028     if (id) {
17029         this.init(id, sGroup, config);
17030         this.initFrame();
17031     }
17032 };
17033
17034 /**
17035  * The default drag frame div id
17036  * @property Roo.dd.DDProxy.dragElId
17037  * @type String
17038  * @static
17039  */
17040 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17041
17042 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17043
17044     /**
17045      * By default we resize the drag frame to be the same size as the element
17046      * we want to drag (this is to get the frame effect).  We can turn it off
17047      * if we want a different behavior.
17048      * @property resizeFrame
17049      * @type boolean
17050      */
17051     resizeFrame: true,
17052
17053     /**
17054      * By default the frame is positioned exactly where the drag element is, so
17055      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17056      * you do not have constraints on the obj is to have the drag frame centered
17057      * around the cursor.  Set centerFrame to true for this effect.
17058      * @property centerFrame
17059      * @type boolean
17060      */
17061     centerFrame: false,
17062
17063     /**
17064      * Creates the proxy element if it does not yet exist
17065      * @method createFrame
17066      */
17067     createFrame: function() {
17068         var self = this;
17069         var body = document.body;
17070
17071         if (!body || !body.firstChild) {
17072             setTimeout( function() { self.createFrame(); }, 50 );
17073             return;
17074         }
17075
17076         var div = this.getDragEl();
17077
17078         if (!div) {
17079             div    = document.createElement("div");
17080             div.id = this.dragElId;
17081             var s  = div.style;
17082
17083             s.position   = "absolute";
17084             s.visibility = "hidden";
17085             s.cursor     = "move";
17086             s.border     = "2px solid #aaa";
17087             s.zIndex     = 999;
17088
17089             // appendChild can blow up IE if invoked prior to the window load event
17090             // while rendering a table.  It is possible there are other scenarios
17091             // that would cause this to happen as well.
17092             body.insertBefore(div, body.firstChild);
17093         }
17094     },
17095
17096     /**
17097      * Initialization for the drag frame element.  Must be called in the
17098      * constructor of all subclasses
17099      * @method initFrame
17100      */
17101     initFrame: function() {
17102         this.createFrame();
17103     },
17104
17105     applyConfig: function() {
17106         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17107
17108         this.resizeFrame = (this.config.resizeFrame !== false);
17109         this.centerFrame = (this.config.centerFrame);
17110         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17111     },
17112
17113     /**
17114      * Resizes the drag frame to the dimensions of the clicked object, positions
17115      * it over the object, and finally displays it
17116      * @method showFrame
17117      * @param {int} iPageX X click position
17118      * @param {int} iPageY Y click position
17119      * @private
17120      */
17121     showFrame: function(iPageX, iPageY) {
17122         var el = this.getEl();
17123         var dragEl = this.getDragEl();
17124         var s = dragEl.style;
17125
17126         this._resizeProxy();
17127
17128         if (this.centerFrame) {
17129             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17130                            Math.round(parseInt(s.height, 10)/2) );
17131         }
17132
17133         this.setDragElPos(iPageX, iPageY);
17134
17135         Roo.fly(dragEl).show();
17136     },
17137
17138     /**
17139      * The proxy is automatically resized to the dimensions of the linked
17140      * element when a drag is initiated, unless resizeFrame is set to false
17141      * @method _resizeProxy
17142      * @private
17143      */
17144     _resizeProxy: function() {
17145         if (this.resizeFrame) {
17146             var el = this.getEl();
17147             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17148         }
17149     },
17150
17151     // overrides Roo.dd.DragDrop
17152     b4MouseDown: function(e) {
17153         var x = e.getPageX();
17154         var y = e.getPageY();
17155         this.autoOffset(x, y);
17156         this.setDragElPos(x, y);
17157     },
17158
17159     // overrides Roo.dd.DragDrop
17160     b4StartDrag: function(x, y) {
17161         // show the drag frame
17162         this.showFrame(x, y);
17163     },
17164
17165     // overrides Roo.dd.DragDrop
17166     b4EndDrag: function(e) {
17167         Roo.fly(this.getDragEl()).hide();
17168     },
17169
17170     // overrides Roo.dd.DragDrop
17171     // By default we try to move the element to the last location of the frame.
17172     // This is so that the default behavior mirrors that of Roo.dd.DD.
17173     endDrag: function(e) {
17174
17175         var lel = this.getEl();
17176         var del = this.getDragEl();
17177
17178         // Show the drag frame briefly so we can get its position
17179         del.style.visibility = "";
17180
17181         this.beforeMove();
17182         // Hide the linked element before the move to get around a Safari
17183         // rendering bug.
17184         lel.style.visibility = "hidden";
17185         Roo.dd.DDM.moveToEl(lel, del);
17186         del.style.visibility = "hidden";
17187         lel.style.visibility = "";
17188
17189         this.afterDrag();
17190     },
17191
17192     beforeMove : function(){
17193
17194     },
17195
17196     afterDrag : function(){
17197
17198     },
17199
17200     toString: function() {
17201         return ("DDProxy " + this.id);
17202     }
17203
17204 });
17205 /*
17206  * Based on:
17207  * Ext JS Library 1.1.1
17208  * Copyright(c) 2006-2007, Ext JS, LLC.
17209  *
17210  * Originally Released Under LGPL - original licence link has changed is not relivant.
17211  *
17212  * Fork - LGPL
17213  * <script type="text/javascript">
17214  */
17215
17216  /**
17217  * @class Roo.dd.DDTarget
17218  * A DragDrop implementation that does not move, but can be a drop
17219  * target.  You would get the same result by simply omitting implementation
17220  * for the event callbacks, but this way we reduce the processing cost of the
17221  * event listener and the callbacks.
17222  * @extends Roo.dd.DragDrop
17223  * @constructor
17224  * @param {String} id the id of the element that is a drop target
17225  * @param {String} sGroup the group of related DragDrop objects
17226  * @param {object} config an object containing configurable attributes
17227  *                 Valid properties for DDTarget in addition to those in
17228  *                 DragDrop:
17229  *                    none
17230  */
17231 Roo.dd.DDTarget = function(id, sGroup, config) {
17232     if (id) {
17233         this.initTarget(id, sGroup, config);
17234     }
17235 };
17236
17237 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17238 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17239     toString: function() {
17240         return ("DDTarget " + this.id);
17241     }
17242 });
17243 /*
17244  * Based on:
17245  * Ext JS Library 1.1.1
17246  * Copyright(c) 2006-2007, Ext JS, LLC.
17247  *
17248  * Originally Released Under LGPL - original licence link has changed is not relivant.
17249  *
17250  * Fork - LGPL
17251  * <script type="text/javascript">
17252  */
17253  
17254
17255 /**
17256  * @class Roo.dd.ScrollManager
17257  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17258  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17259  * @singleton
17260  */
17261 Roo.dd.ScrollManager = function(){
17262     var ddm = Roo.dd.DragDropMgr;
17263     var els = {};
17264     var dragEl = null;
17265     var proc = {};
17266     
17267     var onStop = function(e){
17268         dragEl = null;
17269         clearProc();
17270     };
17271     
17272     var triggerRefresh = function(){
17273         if(ddm.dragCurrent){
17274              ddm.refreshCache(ddm.dragCurrent.groups);
17275         }
17276     };
17277     
17278     var doScroll = function(){
17279         if(ddm.dragCurrent){
17280             var dds = Roo.dd.ScrollManager;
17281             if(!dds.animate){
17282                 if(proc.el.scroll(proc.dir, dds.increment)){
17283                     triggerRefresh();
17284                 }
17285             }else{
17286                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17287             }
17288         }
17289     };
17290     
17291     var clearProc = function(){
17292         if(proc.id){
17293             clearInterval(proc.id);
17294         }
17295         proc.id = 0;
17296         proc.el = null;
17297         proc.dir = "";
17298     };
17299     
17300     var startProc = function(el, dir){
17301         clearProc();
17302         proc.el = el;
17303         proc.dir = dir;
17304         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17305     };
17306     
17307     var onFire = function(e, isDrop){
17308         if(isDrop || !ddm.dragCurrent){ return; }
17309         var dds = Roo.dd.ScrollManager;
17310         if(!dragEl || dragEl != ddm.dragCurrent){
17311             dragEl = ddm.dragCurrent;
17312             // refresh regions on drag start
17313             dds.refreshCache();
17314         }
17315         
17316         var xy = Roo.lib.Event.getXY(e);
17317         var pt = new Roo.lib.Point(xy[0], xy[1]);
17318         for(var id in els){
17319             var el = els[id], r = el._region;
17320             if(r && r.contains(pt) && el.isScrollable()){
17321                 if(r.bottom - pt.y <= dds.thresh){
17322                     if(proc.el != el){
17323                         startProc(el, "down");
17324                     }
17325                     return;
17326                 }else if(r.right - pt.x <= dds.thresh){
17327                     if(proc.el != el){
17328                         startProc(el, "left");
17329                     }
17330                     return;
17331                 }else if(pt.y - r.top <= dds.thresh){
17332                     if(proc.el != el){
17333                         startProc(el, "up");
17334                     }
17335                     return;
17336                 }else if(pt.x - r.left <= dds.thresh){
17337                     if(proc.el != el){
17338                         startProc(el, "right");
17339                     }
17340                     return;
17341                 }
17342             }
17343         }
17344         clearProc();
17345     };
17346     
17347     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17348     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17349     
17350     return {
17351         /**
17352          * Registers new overflow element(s) to auto scroll
17353          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17354          */
17355         register : function(el){
17356             if(el instanceof Array){
17357                 for(var i = 0, len = el.length; i < len; i++) {
17358                         this.register(el[i]);
17359                 }
17360             }else{
17361                 el = Roo.get(el);
17362                 els[el.id] = el;
17363             }
17364         },
17365         
17366         /**
17367          * Unregisters overflow element(s) so they are no longer scrolled
17368          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17369          */
17370         unregister : function(el){
17371             if(el instanceof Array){
17372                 for(var i = 0, len = el.length; i < len; i++) {
17373                         this.unregister(el[i]);
17374                 }
17375             }else{
17376                 el = Roo.get(el);
17377                 delete els[el.id];
17378             }
17379         },
17380         
17381         /**
17382          * The number of pixels from the edge of a container the pointer needs to be to 
17383          * trigger scrolling (defaults to 25)
17384          * @type Number
17385          */
17386         thresh : 25,
17387         
17388         /**
17389          * The number of pixels to scroll in each scroll increment (defaults to 50)
17390          * @type Number
17391          */
17392         increment : 100,
17393         
17394         /**
17395          * The frequency of scrolls in milliseconds (defaults to 500)
17396          * @type Number
17397          */
17398         frequency : 500,
17399         
17400         /**
17401          * True to animate the scroll (defaults to true)
17402          * @type Boolean
17403          */
17404         animate: true,
17405         
17406         /**
17407          * The animation duration in seconds - 
17408          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17409          * @type Number
17410          */
17411         animDuration: .4,
17412         
17413         /**
17414          * Manually trigger a cache refresh.
17415          */
17416         refreshCache : function(){
17417             for(var id in els){
17418                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17419                     els[id]._region = els[id].getRegion();
17420                 }
17421             }
17422         }
17423     };
17424 }();/*
17425  * Based on:
17426  * Ext JS Library 1.1.1
17427  * Copyright(c) 2006-2007, Ext JS, LLC.
17428  *
17429  * Originally Released Under LGPL - original licence link has changed is not relivant.
17430  *
17431  * Fork - LGPL
17432  * <script type="text/javascript">
17433  */
17434  
17435
17436 /**
17437  * @class Roo.dd.Registry
17438  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17439  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17440  * @singleton
17441  */
17442 Roo.dd.Registry = function(){
17443     var elements = {}; 
17444     var handles = {}; 
17445     var autoIdSeed = 0;
17446
17447     var getId = function(el, autogen){
17448         if(typeof el == "string"){
17449             return el;
17450         }
17451         var id = el.id;
17452         if(!id && autogen !== false){
17453             id = "roodd-" + (++autoIdSeed);
17454             el.id = id;
17455         }
17456         return id;
17457     };
17458     
17459     return {
17460     /**
17461      * Register a drag drop element
17462      * @param {String/HTMLElement) element The id or DOM node to register
17463      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17464      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17465      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17466      * populated in the data object (if applicable):
17467      * <pre>
17468 Value      Description<br />
17469 ---------  ------------------------------------------<br />
17470 handles    Array of DOM nodes that trigger dragging<br />
17471            for the element being registered<br />
17472 isHandle   True if the element passed in triggers<br />
17473            dragging itself, else false
17474 </pre>
17475      */
17476         register : function(el, data){
17477             data = data || {};
17478             if(typeof el == "string"){
17479                 el = document.getElementById(el);
17480             }
17481             data.ddel = el;
17482             elements[getId(el)] = data;
17483             if(data.isHandle !== false){
17484                 handles[data.ddel.id] = data;
17485             }
17486             if(data.handles){
17487                 var hs = data.handles;
17488                 for(var i = 0, len = hs.length; i < len; i++){
17489                         handles[getId(hs[i])] = data;
17490                 }
17491             }
17492         },
17493
17494     /**
17495      * Unregister a drag drop element
17496      * @param {String/HTMLElement) element The id or DOM node to unregister
17497      */
17498         unregister : function(el){
17499             var id = getId(el, false);
17500             var data = elements[id];
17501             if(data){
17502                 delete elements[id];
17503                 if(data.handles){
17504                     var hs = data.handles;
17505                     for(var i = 0, len = hs.length; i < len; i++){
17506                         delete handles[getId(hs[i], false)];
17507                     }
17508                 }
17509             }
17510         },
17511
17512     /**
17513      * Returns the handle registered for a DOM Node by id
17514      * @param {String/HTMLElement} id The DOM node or id to look up
17515      * @return {Object} handle The custom handle data
17516      */
17517         getHandle : function(id){
17518             if(typeof id != "string"){ // must be element?
17519                 id = id.id;
17520             }
17521             return handles[id];
17522         },
17523
17524     /**
17525      * Returns the handle that is registered for the DOM node that is the target of the event
17526      * @param {Event} e The event
17527      * @return {Object} handle The custom handle data
17528      */
17529         getHandleFromEvent : function(e){
17530             var t = Roo.lib.Event.getTarget(e);
17531             return t ? handles[t.id] : null;
17532         },
17533
17534     /**
17535      * Returns a custom data object that is registered for a DOM node by id
17536      * @param {String/HTMLElement} id The DOM node or id to look up
17537      * @return {Object} data The custom data
17538      */
17539         getTarget : function(id){
17540             if(typeof id != "string"){ // must be element?
17541                 id = id.id;
17542             }
17543             return elements[id];
17544         },
17545
17546     /**
17547      * Returns a custom data object that is registered for the DOM node that is the target of the event
17548      * @param {Event} e The event
17549      * @return {Object} data The custom data
17550      */
17551         getTargetFromEvent : function(e){
17552             var t = Roo.lib.Event.getTarget(e);
17553             return t ? elements[t.id] || handles[t.id] : null;
17554         }
17555     };
17556 }();/*
17557  * Based on:
17558  * Ext JS Library 1.1.1
17559  * Copyright(c) 2006-2007, Ext JS, LLC.
17560  *
17561  * Originally Released Under LGPL - original licence link has changed is not relivant.
17562  *
17563  * Fork - LGPL
17564  * <script type="text/javascript">
17565  */
17566  
17567
17568 /**
17569  * @class Roo.dd.StatusProxy
17570  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17571  * default drag proxy used by all Roo.dd components.
17572  * @constructor
17573  * @param {Object} config
17574  */
17575 Roo.dd.StatusProxy = function(config){
17576     Roo.apply(this, config);
17577     this.id = this.id || Roo.id();
17578     this.el = new Roo.Layer({
17579         dh: {
17580             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17581                 {tag: "div", cls: "x-dd-drop-icon"},
17582                 {tag: "div", cls: "x-dd-drag-ghost"}
17583             ]
17584         }, 
17585         shadow: !config || config.shadow !== false
17586     });
17587     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17588     this.dropStatus = this.dropNotAllowed;
17589 };
17590
17591 Roo.dd.StatusProxy.prototype = {
17592     /**
17593      * @cfg {String} dropAllowed
17594      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17595      */
17596     dropAllowed : "x-dd-drop-ok",
17597     /**
17598      * @cfg {String} dropNotAllowed
17599      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17600      */
17601     dropNotAllowed : "x-dd-drop-nodrop",
17602
17603     /**
17604      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17605      * over the current target element.
17606      * @param {String} cssClass The css class for the new drop status indicator image
17607      */
17608     setStatus : function(cssClass){
17609         cssClass = cssClass || this.dropNotAllowed;
17610         if(this.dropStatus != cssClass){
17611             this.el.replaceClass(this.dropStatus, cssClass);
17612             this.dropStatus = cssClass;
17613         }
17614     },
17615
17616     /**
17617      * Resets the status indicator to the default dropNotAllowed value
17618      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17619      */
17620     reset : function(clearGhost){
17621         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17622         this.dropStatus = this.dropNotAllowed;
17623         if(clearGhost){
17624             this.ghost.update("");
17625         }
17626     },
17627
17628     /**
17629      * Updates the contents of the ghost element
17630      * @param {String} html The html that will replace the current innerHTML of the ghost element
17631      */
17632     update : function(html){
17633         if(typeof html == "string"){
17634             this.ghost.update(html);
17635         }else{
17636             this.ghost.update("");
17637             html.style.margin = "0";
17638             this.ghost.dom.appendChild(html);
17639         }
17640         // ensure float = none set?? cant remember why though.
17641         var el = this.ghost.dom.firstChild;
17642                 if(el){
17643                         Roo.fly(el).setStyle('float', 'none');
17644                 }
17645     },
17646     
17647     /**
17648      * Returns the underlying proxy {@link Roo.Layer}
17649      * @return {Roo.Layer} el
17650     */
17651     getEl : function(){
17652         return this.el;
17653     },
17654
17655     /**
17656      * Returns the ghost element
17657      * @return {Roo.Element} el
17658      */
17659     getGhost : function(){
17660         return this.ghost;
17661     },
17662
17663     /**
17664      * Hides the proxy
17665      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17666      */
17667     hide : function(clear){
17668         this.el.hide();
17669         if(clear){
17670             this.reset(true);
17671         }
17672     },
17673
17674     /**
17675      * Stops the repair animation if it's currently running
17676      */
17677     stop : function(){
17678         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17679             this.anim.stop();
17680         }
17681     },
17682
17683     /**
17684      * Displays this proxy
17685      */
17686     show : function(){
17687         this.el.show();
17688     },
17689
17690     /**
17691      * Force the Layer to sync its shadow and shim positions to the element
17692      */
17693     sync : function(){
17694         this.el.sync();
17695     },
17696
17697     /**
17698      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17699      * invalid drop operation by the item being dragged.
17700      * @param {Array} xy The XY position of the element ([x, y])
17701      * @param {Function} callback The function to call after the repair is complete
17702      * @param {Object} scope The scope in which to execute the callback
17703      */
17704     repair : function(xy, callback, scope){
17705         this.callback = callback;
17706         this.scope = scope;
17707         if(xy && this.animRepair !== false){
17708             this.el.addClass("x-dd-drag-repair");
17709             this.el.hideUnders(true);
17710             this.anim = this.el.shift({
17711                 duration: this.repairDuration || .5,
17712                 easing: 'easeOut',
17713                 xy: xy,
17714                 stopFx: true,
17715                 callback: this.afterRepair,
17716                 scope: this
17717             });
17718         }else{
17719             this.afterRepair();
17720         }
17721     },
17722
17723     // private
17724     afterRepair : function(){
17725         this.hide(true);
17726         if(typeof this.callback == "function"){
17727             this.callback.call(this.scope || this);
17728         }
17729         this.callback = null;
17730         this.scope = null;
17731     }
17732 };/*
17733  * Based on:
17734  * Ext JS Library 1.1.1
17735  * Copyright(c) 2006-2007, Ext JS, LLC.
17736  *
17737  * Originally Released Under LGPL - original licence link has changed is not relivant.
17738  *
17739  * Fork - LGPL
17740  * <script type="text/javascript">
17741  */
17742
17743 /**
17744  * @class Roo.dd.DragSource
17745  * @extends Roo.dd.DDProxy
17746  * A simple class that provides the basic implementation needed to make any element draggable.
17747  * @constructor
17748  * @param {String/HTMLElement/Element} el The container element
17749  * @param {Object} config
17750  */
17751 Roo.dd.DragSource = function(el, config){
17752     this.el = Roo.get(el);
17753     this.dragData = {};
17754     
17755     Roo.apply(this, config);
17756     
17757     if(!this.proxy){
17758         this.proxy = new Roo.dd.StatusProxy();
17759     }
17760
17761     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17762           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17763     
17764     this.dragging = false;
17765 };
17766
17767 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17768     /**
17769      * @cfg {String} dropAllowed
17770      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17771      */
17772     dropAllowed : "x-dd-drop-ok",
17773     /**
17774      * @cfg {String} dropNotAllowed
17775      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17776      */
17777     dropNotAllowed : "x-dd-drop-nodrop",
17778
17779     /**
17780      * Returns the data object associated with this drag source
17781      * @return {Object} data An object containing arbitrary data
17782      */
17783     getDragData : function(e){
17784         return this.dragData;
17785     },
17786
17787     // private
17788     onDragEnter : function(e, id){
17789         var target = Roo.dd.DragDropMgr.getDDById(id);
17790         this.cachedTarget = target;
17791         if(this.beforeDragEnter(target, e, id) !== false){
17792             if(target.isNotifyTarget){
17793                 var status = target.notifyEnter(this, e, this.dragData);
17794                 this.proxy.setStatus(status);
17795             }else{
17796                 this.proxy.setStatus(this.dropAllowed);
17797             }
17798             
17799             if(this.afterDragEnter){
17800                 /**
17801                  * An empty function by default, but provided so that you can perform a custom action
17802                  * when the dragged item enters the drop target by providing an implementation.
17803                  * @param {Roo.dd.DragDrop} target The drop target
17804                  * @param {Event} e The event object
17805                  * @param {String} id The id of the dragged element
17806                  * @method afterDragEnter
17807                  */
17808                 this.afterDragEnter(target, e, id);
17809             }
17810         }
17811     },
17812
17813     /**
17814      * An empty function by default, but provided so that you can perform a custom action
17815      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17816      * @param {Roo.dd.DragDrop} target The drop target
17817      * @param {Event} e The event object
17818      * @param {String} id The id of the dragged element
17819      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17820      */
17821     beforeDragEnter : function(target, e, id){
17822         return true;
17823     },
17824
17825     // private
17826     alignElWithMouse: function() {
17827         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17828         this.proxy.sync();
17829     },
17830
17831     // private
17832     onDragOver : function(e, id){
17833         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17834         if(this.beforeDragOver(target, e, id) !== false){
17835             if(target.isNotifyTarget){
17836                 var status = target.notifyOver(this, e, this.dragData);
17837                 this.proxy.setStatus(status);
17838             }
17839
17840             if(this.afterDragOver){
17841                 /**
17842                  * An empty function by default, but provided so that you can perform a custom action
17843                  * while the dragged item is over the drop target by providing an implementation.
17844                  * @param {Roo.dd.DragDrop} target The drop target
17845                  * @param {Event} e The event object
17846                  * @param {String} id The id of the dragged element
17847                  * @method afterDragOver
17848                  */
17849                 this.afterDragOver(target, e, id);
17850             }
17851         }
17852     },
17853
17854     /**
17855      * An empty function by default, but provided so that you can perform a custom action
17856      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17857      * @param {Roo.dd.DragDrop} target The drop target
17858      * @param {Event} e The event object
17859      * @param {String} id The id of the dragged element
17860      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17861      */
17862     beforeDragOver : function(target, e, id){
17863         return true;
17864     },
17865
17866     // private
17867     onDragOut : function(e, id){
17868         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17869         if(this.beforeDragOut(target, e, id) !== false){
17870             if(target.isNotifyTarget){
17871                 target.notifyOut(this, e, this.dragData);
17872             }
17873             this.proxy.reset();
17874             if(this.afterDragOut){
17875                 /**
17876                  * An empty function by default, but provided so that you can perform a custom action
17877                  * after the dragged item is dragged out of the target without dropping.
17878                  * @param {Roo.dd.DragDrop} target The drop target
17879                  * @param {Event} e The event object
17880                  * @param {String} id The id of the dragged element
17881                  * @method afterDragOut
17882                  */
17883                 this.afterDragOut(target, e, id);
17884             }
17885         }
17886         this.cachedTarget = null;
17887     },
17888
17889     /**
17890      * An empty function by default, but provided so that you can perform a custom action before the dragged
17891      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17892      * @param {Roo.dd.DragDrop} target The drop target
17893      * @param {Event} e The event object
17894      * @param {String} id The id of the dragged element
17895      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17896      */
17897     beforeDragOut : function(target, e, id){
17898         return true;
17899     },
17900     
17901     // private
17902     onDragDrop : function(e, id){
17903         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17904         if(this.beforeDragDrop(target, e, id) !== false){
17905             if(target.isNotifyTarget){
17906                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17907                     this.onValidDrop(target, e, id);
17908                 }else{
17909                     this.onInvalidDrop(target, e, id);
17910                 }
17911             }else{
17912                 this.onValidDrop(target, e, id);
17913             }
17914             
17915             if(this.afterDragDrop){
17916                 /**
17917                  * An empty function by default, but provided so that you can perform a custom action
17918                  * after a valid drag drop has occurred by providing an implementation.
17919                  * @param {Roo.dd.DragDrop} target The drop target
17920                  * @param {Event} e The event object
17921                  * @param {String} id The id of the dropped element
17922                  * @method afterDragDrop
17923                  */
17924                 this.afterDragDrop(target, e, id);
17925             }
17926         }
17927         delete this.cachedTarget;
17928     },
17929
17930     /**
17931      * An empty function by default, but provided so that you can perform a custom action before the dragged
17932      * item is dropped onto the target and optionally cancel the onDragDrop.
17933      * @param {Roo.dd.DragDrop} target The drop target
17934      * @param {Event} e The event object
17935      * @param {String} id The id of the dragged element
17936      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17937      */
17938     beforeDragDrop : function(target, e, id){
17939         return true;
17940     },
17941
17942     // private
17943     onValidDrop : function(target, e, id){
17944         this.hideProxy();
17945         if(this.afterValidDrop){
17946             /**
17947              * An empty function by default, but provided so that you can perform a custom action
17948              * after a valid drop has occurred by providing an implementation.
17949              * @param {Object} target The target DD 
17950              * @param {Event} e The event object
17951              * @param {String} id The id of the dropped element
17952              * @method afterInvalidDrop
17953              */
17954             this.afterValidDrop(target, e, id);
17955         }
17956     },
17957
17958     // private
17959     getRepairXY : function(e, data){
17960         return this.el.getXY();  
17961     },
17962
17963     // private
17964     onInvalidDrop : function(target, e, id){
17965         this.beforeInvalidDrop(target, e, id);
17966         if(this.cachedTarget){
17967             if(this.cachedTarget.isNotifyTarget){
17968                 this.cachedTarget.notifyOut(this, e, this.dragData);
17969             }
17970             this.cacheTarget = null;
17971         }
17972         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
17973
17974         if(this.afterInvalidDrop){
17975             /**
17976              * An empty function by default, but provided so that you can perform a custom action
17977              * after an invalid drop has occurred by providing an implementation.
17978              * @param {Event} e The event object
17979              * @param {String} id The id of the dropped element
17980              * @method afterInvalidDrop
17981              */
17982             this.afterInvalidDrop(e, id);
17983         }
17984     },
17985
17986     // private
17987     afterRepair : function(){
17988         if(Roo.enableFx){
17989             this.el.highlight(this.hlColor || "c3daf9");
17990         }
17991         this.dragging = false;
17992     },
17993
17994     /**
17995      * An empty function by default, but provided so that you can perform a custom action after an invalid
17996      * drop has occurred.
17997      * @param {Roo.dd.DragDrop} target The drop target
17998      * @param {Event} e The event object
17999      * @param {String} id The id of the dragged element
18000      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18001      */
18002     beforeInvalidDrop : function(target, e, id){
18003         return true;
18004     },
18005
18006     // private
18007     handleMouseDown : function(e){
18008         if(this.dragging) {
18009             return;
18010         }
18011         var data = this.getDragData(e);
18012         if(data && this.onBeforeDrag(data, e) !== false){
18013             this.dragData = data;
18014             this.proxy.stop();
18015             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18016         } 
18017     },
18018
18019     /**
18020      * An empty function by default, but provided so that you can perform a custom action before the initial
18021      * drag event begins and optionally cancel it.
18022      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18023      * @param {Event} e The event object
18024      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18025      */
18026     onBeforeDrag : function(data, e){
18027         return true;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action once the initial
18032      * drag event has begun.  The drag cannot be canceled from this function.
18033      * @param {Number} x The x position of the click on the dragged object
18034      * @param {Number} y The y position of the click on the dragged object
18035      */
18036     onStartDrag : Roo.emptyFn,
18037
18038     // private - YUI override
18039     startDrag : function(x, y){
18040         this.proxy.reset();
18041         this.dragging = true;
18042         this.proxy.update("");
18043         this.onInitDrag(x, y);
18044         this.proxy.show();
18045     },
18046
18047     // private
18048     onInitDrag : function(x, y){
18049         var clone = this.el.dom.cloneNode(true);
18050         clone.id = Roo.id(); // prevent duplicate ids
18051         this.proxy.update(clone);
18052         this.onStartDrag(x, y);
18053         return true;
18054     },
18055
18056     /**
18057      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18058      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18059      */
18060     getProxy : function(){
18061         return this.proxy;  
18062     },
18063
18064     /**
18065      * Hides the drag source's {@link Roo.dd.StatusProxy}
18066      */
18067     hideProxy : function(){
18068         this.proxy.hide();  
18069         this.proxy.reset(true);
18070         this.dragging = false;
18071     },
18072
18073     // private
18074     triggerCacheRefresh : function(){
18075         Roo.dd.DDM.refreshCache(this.groups);
18076     },
18077
18078     // private - override to prevent hiding
18079     b4EndDrag: function(e) {
18080     },
18081
18082     // private - override to prevent moving
18083     endDrag : function(e){
18084         this.onEndDrag(this.dragData, e);
18085     },
18086
18087     // private
18088     onEndDrag : function(data, e){
18089     },
18090     
18091     // private - pin to cursor
18092     autoOffset : function(x, y) {
18093         this.setDelta(-12, -20);
18094     }    
18095 });/*
18096  * Based on:
18097  * Ext JS Library 1.1.1
18098  * Copyright(c) 2006-2007, Ext JS, LLC.
18099  *
18100  * Originally Released Under LGPL - original licence link has changed is not relivant.
18101  *
18102  * Fork - LGPL
18103  * <script type="text/javascript">
18104  */
18105
18106
18107 /**
18108  * @class Roo.dd.DropTarget
18109  * @extends Roo.dd.DDTarget
18110  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18111  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18112  * @constructor
18113  * @param {String/HTMLElement/Element} el The container element
18114  * @param {Object} config
18115  */
18116 Roo.dd.DropTarget = function(el, config){
18117     this.el = Roo.get(el);
18118     
18119     Roo.apply(this, config);
18120     
18121     if(this.containerScroll){
18122         Roo.dd.ScrollManager.register(this.el);
18123     }
18124     
18125     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18126           {isTarget: true});
18127
18128 };
18129
18130 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18131     /**
18132      * @cfg {String} overClass
18133      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18134      */
18135     /**
18136      * @cfg {String} dropAllowed
18137      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18138      */
18139     dropAllowed : "x-dd-drop-ok",
18140     /**
18141      * @cfg {String} dropNotAllowed
18142      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18143      */
18144     dropNotAllowed : "x-dd-drop-nodrop",
18145
18146     // private
18147     isTarget : true,
18148
18149     // private
18150     isNotifyTarget : true,
18151
18152     /**
18153      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18154      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18155      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18156      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18157      * @param {Event} e The event
18158      * @param {Object} data An object containing arbitrary data supplied by the drag source
18159      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18160      * underlying {@link Roo.dd.StatusProxy} can be updated
18161      */
18162     notifyEnter : function(dd, e, data){
18163         if(this.overClass){
18164             this.el.addClass(this.overClass);
18165         }
18166         return this.dropAllowed;
18167     },
18168
18169     /**
18170      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18171      * This method will be called on every mouse movement while the drag source is over the drop target.
18172      * This default implementation simply returns the dropAllowed config value.
18173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18174      * @param {Event} e The event
18175      * @param {Object} data An object containing arbitrary data supplied by the drag source
18176      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18177      * underlying {@link Roo.dd.StatusProxy} can be updated
18178      */
18179     notifyOver : function(dd, e, data){
18180         return this.dropAllowed;
18181     },
18182
18183     /**
18184      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18185      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18186      * overClass (if any) from the drop element.
18187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18188      * @param {Event} e The event
18189      * @param {Object} data An object containing arbitrary data supplied by the drag source
18190      */
18191     notifyOut : function(dd, e, data){
18192         if(this.overClass){
18193             this.el.removeClass(this.overClass);
18194         }
18195     },
18196
18197     /**
18198      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18199      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18200      * implementation that does something to process the drop event and returns true so that the drag source's
18201      * repair action does not run.
18202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18203      * @param {Event} e The event
18204      * @param {Object} data An object containing arbitrary data supplied by the drag source
18205      * @return {Boolean} True if the drop was valid, else false
18206      */
18207     notifyDrop : function(dd, e, data){
18208         return false;
18209     }
18210 });/*
18211  * Based on:
18212  * Ext JS Library 1.1.1
18213  * Copyright(c) 2006-2007, Ext JS, LLC.
18214  *
18215  * Originally Released Under LGPL - original licence link has changed is not relivant.
18216  *
18217  * Fork - LGPL
18218  * <script type="text/javascript">
18219  */
18220
18221
18222 /**
18223  * @class Roo.dd.DragZone
18224  * @extends Roo.dd.DragSource
18225  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18226  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18227  * @constructor
18228  * @param {String/HTMLElement/Element} el The container element
18229  * @param {Object} config
18230  */
18231 Roo.dd.DragZone = function(el, config){
18232     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18233     if(this.containerScroll){
18234         Roo.dd.ScrollManager.register(this.el);
18235     }
18236 };
18237
18238 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18239     /**
18240      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18241      * for auto scrolling during drag operations.
18242      */
18243     /**
18244      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18245      * method after a failed drop (defaults to "c3daf9" - light blue)
18246      */
18247
18248     /**
18249      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18250      * for a valid target to drag based on the mouse down. Override this method
18251      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18252      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18253      * @param {EventObject} e The mouse down event
18254      * @return {Object} The dragData
18255      */
18256     getDragData : function(e){
18257         return Roo.dd.Registry.getHandleFromEvent(e);
18258     },
18259     
18260     /**
18261      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18262      * this.dragData.ddel
18263      * @param {Number} x The x position of the click on the dragged object
18264      * @param {Number} y The y position of the click on the dragged object
18265      * @return {Boolean} true to continue the drag, false to cancel
18266      */
18267     onInitDrag : function(x, y){
18268         this.proxy.update(this.dragData.ddel.cloneNode(true));
18269         this.onStartDrag(x, y);
18270         return true;
18271     },
18272     
18273     /**
18274      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18275      */
18276     afterRepair : function(){
18277         if(Roo.enableFx){
18278             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18279         }
18280         this.dragging = false;
18281     },
18282
18283     /**
18284      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18285      * the XY of this.dragData.ddel
18286      * @param {EventObject} e The mouse up event
18287      * @return {Array} The xy location (e.g. [100, 200])
18288      */
18289     getRepairXY : function(e){
18290         return Roo.Element.fly(this.dragData.ddel).getXY();  
18291     }
18292 });/*
18293  * Based on:
18294  * Ext JS Library 1.1.1
18295  * Copyright(c) 2006-2007, Ext JS, LLC.
18296  *
18297  * Originally Released Under LGPL - original licence link has changed is not relivant.
18298  *
18299  * Fork - LGPL
18300  * <script type="text/javascript">
18301  */
18302 /**
18303  * @class Roo.dd.DropZone
18304  * @extends Roo.dd.DropTarget
18305  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18306  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18307  * @constructor
18308  * @param {String/HTMLElement/Element} el The container element
18309  * @param {Object} config
18310  */
18311 Roo.dd.DropZone = function(el, config){
18312     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18313 };
18314
18315 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18316     /**
18317      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18318      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18319      * provide your own custom lookup.
18320      * @param {Event} e The event
18321      * @return {Object} data The custom data
18322      */
18323     getTargetFromEvent : function(e){
18324         return Roo.dd.Registry.getTargetFromEvent(e);
18325     },
18326
18327     /**
18328      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18329      * that it has registered.  This method has no default implementation and should be overridden to provide
18330      * node-specific processing if necessary.
18331      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18332      * {@link #getTargetFromEvent} for this node)
18333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18334      * @param {Event} e The event
18335      * @param {Object} data An object containing arbitrary data supplied by the drag source
18336      */
18337     onNodeEnter : function(n, dd, e, data){
18338         
18339     },
18340
18341     /**
18342      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18343      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18344      * overridden to provide the proper feedback.
18345      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18346      * {@link #getTargetFromEvent} for this node)
18347      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18348      * @param {Event} e The event
18349      * @param {Object} data An object containing arbitrary data supplied by the drag source
18350      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18351      * underlying {@link Roo.dd.StatusProxy} can be updated
18352      */
18353     onNodeOver : function(n, dd, e, data){
18354         return this.dropAllowed;
18355     },
18356
18357     /**
18358      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18359      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18360      * node-specific processing if necessary.
18361      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18362      * {@link #getTargetFromEvent} for this node)
18363      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18364      * @param {Event} e The event
18365      * @param {Object} data An object containing arbitrary data supplied by the drag source
18366      */
18367     onNodeOut : function(n, dd, e, data){
18368         
18369     },
18370
18371     /**
18372      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18373      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18374      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18375      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18376      * {@link #getTargetFromEvent} for this node)
18377      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18378      * @param {Event} e The event
18379      * @param {Object} data An object containing arbitrary data supplied by the drag source
18380      * @return {Boolean} True if the drop was valid, else false
18381      */
18382     onNodeDrop : function(n, dd, e, data){
18383         return false;
18384     },
18385
18386     /**
18387      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18388      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18389      * it should be overridden to provide the proper feedback if necessary.
18390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18391      * @param {Event} e The event
18392      * @param {Object} data An object containing arbitrary data supplied by the drag source
18393      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18394      * underlying {@link Roo.dd.StatusProxy} can be updated
18395      */
18396     onContainerOver : function(dd, e, data){
18397         return this.dropNotAllowed;
18398     },
18399
18400     /**
18401      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18402      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18403      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18404      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18405      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18406      * @param {Event} e The event
18407      * @param {Object} data An object containing arbitrary data supplied by the drag source
18408      * @return {Boolean} True if the drop was valid, else false
18409      */
18410     onContainerDrop : function(dd, e, data){
18411         return false;
18412     },
18413
18414     /**
18415      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18416      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18417      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18418      * you should override this method and provide a custom implementation.
18419      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18420      * @param {Event} e The event
18421      * @param {Object} data An object containing arbitrary data supplied by the drag source
18422      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18423      * underlying {@link Roo.dd.StatusProxy} can be updated
18424      */
18425     notifyEnter : function(dd, e, data){
18426         return this.dropNotAllowed;
18427     },
18428
18429     /**
18430      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18431      * This method will be called on every mouse movement while the drag source is over the drop zone.
18432      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18433      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18434      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18435      * registered node, it will call {@link #onContainerOver}.
18436      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18437      * @param {Event} e The event
18438      * @param {Object} data An object containing arbitrary data supplied by the drag source
18439      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18440      * underlying {@link Roo.dd.StatusProxy} can be updated
18441      */
18442     notifyOver : function(dd, e, data){
18443         var n = this.getTargetFromEvent(e);
18444         if(!n){ // not over valid drop target
18445             if(this.lastOverNode){
18446                 this.onNodeOut(this.lastOverNode, dd, e, data);
18447                 this.lastOverNode = null;
18448             }
18449             return this.onContainerOver(dd, e, data);
18450         }
18451         if(this.lastOverNode != n){
18452             if(this.lastOverNode){
18453                 this.onNodeOut(this.lastOverNode, dd, e, data);
18454             }
18455             this.onNodeEnter(n, dd, e, data);
18456             this.lastOverNode = n;
18457         }
18458         return this.onNodeOver(n, dd, e, data);
18459     },
18460
18461     /**
18462      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18463      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18464      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18465      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18466      * @param {Event} e The event
18467      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18468      */
18469     notifyOut : function(dd, e, data){
18470         if(this.lastOverNode){
18471             this.onNodeOut(this.lastOverNode, dd, e, data);
18472             this.lastOverNode = null;
18473         }
18474     },
18475
18476     /**
18477      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18478      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18479      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18480      * otherwise it will call {@link #onContainerDrop}.
18481      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18482      * @param {Event} e The event
18483      * @param {Object} data An object containing arbitrary data supplied by the drag source
18484      * @return {Boolean} True if the drop was valid, else false
18485      */
18486     notifyDrop : function(dd, e, data){
18487         if(this.lastOverNode){
18488             this.onNodeOut(this.lastOverNode, dd, e, data);
18489             this.lastOverNode = null;
18490         }
18491         var n = this.getTargetFromEvent(e);
18492         return n ?
18493             this.onNodeDrop(n, dd, e, data) :
18494             this.onContainerDrop(dd, e, data);
18495     },
18496
18497     // private
18498     triggerCacheRefresh : function(){
18499         Roo.dd.DDM.refreshCache(this.groups);
18500     }  
18501 });/*
18502  * Based on:
18503  * Ext JS Library 1.1.1
18504  * Copyright(c) 2006-2007, Ext JS, LLC.
18505  *
18506  * Originally Released Under LGPL - original licence link has changed is not relivant.
18507  *
18508  * Fork - LGPL
18509  * <script type="text/javascript">
18510  */
18511
18512
18513 /**
18514  * @class Roo.data.SortTypes
18515  * @singleton
18516  * Defines the default sorting (casting?) comparison functions used when sorting data.
18517  */
18518 Roo.data.SortTypes = {
18519     /**
18520      * Default sort that does nothing
18521      * @param {Mixed} s The value being converted
18522      * @return {Mixed} The comparison value
18523      */
18524     none : function(s){
18525         return s;
18526     },
18527     
18528     /**
18529      * The regular expression used to strip tags
18530      * @type {RegExp}
18531      * @property
18532      */
18533     stripTagsRE : /<\/?[^>]+>/gi,
18534     
18535     /**
18536      * Strips all HTML tags to sort on text only
18537      * @param {Mixed} s The value being converted
18538      * @return {String} The comparison value
18539      */
18540     asText : function(s){
18541         return String(s).replace(this.stripTagsRE, "");
18542     },
18543     
18544     /**
18545      * Strips all HTML tags to sort on text only - Case insensitive
18546      * @param {Mixed} s The value being converted
18547      * @return {String} The comparison value
18548      */
18549     asUCText : function(s){
18550         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18551     },
18552     
18553     /**
18554      * Case insensitive string
18555      * @param {Mixed} s The value being converted
18556      * @return {String} The comparison value
18557      */
18558     asUCString : function(s) {
18559         return String(s).toUpperCase();
18560     },
18561     
18562     /**
18563      * Date sorting
18564      * @param {Mixed} s The value being converted
18565      * @return {Number} The comparison value
18566      */
18567     asDate : function(s) {
18568         if(!s){
18569             return 0;
18570         }
18571         if(s instanceof Date){
18572             return s.getTime();
18573         }
18574         return Date.parse(String(s));
18575     },
18576     
18577     /**
18578      * Float sorting
18579      * @param {Mixed} s The value being converted
18580      * @return {Float} The comparison value
18581      */
18582     asFloat : function(s) {
18583         var val = parseFloat(String(s).replace(/,/g, ""));
18584         if(isNaN(val)) val = 0;
18585         return val;
18586     },
18587     
18588     /**
18589      * Integer sorting
18590      * @param {Mixed} s The value being converted
18591      * @return {Number} The comparison value
18592      */
18593     asInt : function(s) {
18594         var val = parseInt(String(s).replace(/,/g, ""));
18595         if(isNaN(val)) val = 0;
18596         return val;
18597     }
18598 };/*
18599  * Based on:
18600  * Ext JS Library 1.1.1
18601  * Copyright(c) 2006-2007, Ext JS, LLC.
18602  *
18603  * Originally Released Under LGPL - original licence link has changed is not relivant.
18604  *
18605  * Fork - LGPL
18606  * <script type="text/javascript">
18607  */
18608
18609 /**
18610 * @class Roo.data.Record
18611  * Instances of this class encapsulate both record <em>definition</em> information, and record
18612  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18613  * to access Records cached in an {@link Roo.data.Store} object.<br>
18614  * <p>
18615  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18616  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18617  * objects.<br>
18618  * <p>
18619  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18620  * @constructor
18621  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18622  * {@link #create}. The parameters are the same.
18623  * @param {Array} data An associative Array of data values keyed by the field name.
18624  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18625  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18626  * not specified an integer id is generated.
18627  */
18628 Roo.data.Record = function(data, id){
18629     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18630     this.data = data;
18631 };
18632
18633 /**
18634  * Generate a constructor for a specific record layout.
18635  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18636  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18637  * Each field definition object may contain the following properties: <ul>
18638  * <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,
18639  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18640  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18641  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18642  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18643  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18644  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18645  * this may be omitted.</p></li>
18646  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18647  * <ul><li>auto (Default, implies no conversion)</li>
18648  * <li>string</li>
18649  * <li>int</li>
18650  * <li>float</li>
18651  * <li>boolean</li>
18652  * <li>date</li></ul></p></li>
18653  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18654  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18655  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18656  * by the Reader into an object that will be stored in the Record. It is passed the
18657  * following parameters:<ul>
18658  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18659  * </ul></p></li>
18660  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18661  * </ul>
18662  * <br>usage:<br><pre><code>
18663 var TopicRecord = Roo.data.Record.create(
18664     {name: 'title', mapping: 'topic_title'},
18665     {name: 'author', mapping: 'username'},
18666     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18667     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18668     {name: 'lastPoster', mapping: 'user2'},
18669     {name: 'excerpt', mapping: 'post_text'}
18670 );
18671
18672 var myNewRecord = new TopicRecord({
18673     title: 'Do my job please',
18674     author: 'noobie',
18675     totalPosts: 1,
18676     lastPost: new Date(),
18677     lastPoster: 'Animal',
18678     excerpt: 'No way dude!'
18679 });
18680 myStore.add(myNewRecord);
18681 </code></pre>
18682  * @method create
18683  * @static
18684  */
18685 Roo.data.Record.create = function(o){
18686     var f = function(){
18687         f.superclass.constructor.apply(this, arguments);
18688     };
18689     Roo.extend(f, Roo.data.Record);
18690     var p = f.prototype;
18691     p.fields = new Roo.util.MixedCollection(false, function(field){
18692         return field.name;
18693     });
18694     for(var i = 0, len = o.length; i < len; i++){
18695         p.fields.add(new Roo.data.Field(o[i]));
18696     }
18697     f.getField = function(name){
18698         return p.fields.get(name);  
18699     };
18700     return f;
18701 };
18702
18703 Roo.data.Record.AUTO_ID = 1000;
18704 Roo.data.Record.EDIT = 'edit';
18705 Roo.data.Record.REJECT = 'reject';
18706 Roo.data.Record.COMMIT = 'commit';
18707
18708 Roo.data.Record.prototype = {
18709     /**
18710      * Readonly flag - true if this record has been modified.
18711      * @type Boolean
18712      */
18713     dirty : false,
18714     editing : false,
18715     error: null,
18716     modified: null,
18717
18718     // private
18719     join : function(store){
18720         this.store = store;
18721     },
18722
18723     /**
18724      * Set the named field to the specified value.
18725      * @param {String} name The name of the field to set.
18726      * @param {Object} value The value to set the field to.
18727      */
18728     set : function(name, value){
18729         if(this.data[name] == value){
18730             return;
18731         }
18732         this.dirty = true;
18733         if(!this.modified){
18734             this.modified = {};
18735         }
18736         if(typeof this.modified[name] == 'undefined'){
18737             this.modified[name] = this.data[name];
18738         }
18739         this.data[name] = value;
18740         if(!this.editing){
18741             this.store.afterEdit(this);
18742         }       
18743     },
18744
18745     /**
18746      * Get the value of the named field.
18747      * @param {String} name The name of the field to get the value of.
18748      * @return {Object} The value of the field.
18749      */
18750     get : function(name){
18751         return this.data[name]; 
18752     },
18753
18754     // private
18755     beginEdit : function(){
18756         this.editing = true;
18757         this.modified = {}; 
18758     },
18759
18760     // private
18761     cancelEdit : function(){
18762         this.editing = false;
18763         delete this.modified;
18764     },
18765
18766     // private
18767     endEdit : function(){
18768         this.editing = false;
18769         if(this.dirty && this.store){
18770             this.store.afterEdit(this);
18771         }
18772     },
18773
18774     /**
18775      * Usually called by the {@link Roo.data.Store} which owns the Record.
18776      * Rejects all changes made to the Record since either creation, or the last commit operation.
18777      * Modified fields are reverted to their original values.
18778      * <p>
18779      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18780      * of reject operations.
18781      */
18782     reject : function(){
18783         var m = this.modified;
18784         for(var n in m){
18785             if(typeof m[n] != "function"){
18786                 this.data[n] = m[n];
18787             }
18788         }
18789         this.dirty = false;
18790         delete this.modified;
18791         this.editing = false;
18792         if(this.store){
18793             this.store.afterReject(this);
18794         }
18795     },
18796
18797     /**
18798      * Usually called by the {@link Roo.data.Store} which owns the Record.
18799      * Commits all changes made to the Record since either creation, or the last commit operation.
18800      * <p>
18801      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18802      * of commit operations.
18803      */
18804     commit : function(){
18805         this.dirty = false;
18806         delete this.modified;
18807         this.editing = false;
18808         if(this.store){
18809             this.store.afterCommit(this);
18810         }
18811     },
18812
18813     // private
18814     hasError : function(){
18815         return this.error != null;
18816     },
18817
18818     // private
18819     clearError : function(){
18820         this.error = null;
18821     },
18822
18823     /**
18824      * Creates a copy of this record.
18825      * @param {String} id (optional) A new record id if you don't want to use this record's id
18826      * @return {Record}
18827      */
18828     copy : function(newId) {
18829         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18830     }
18831 };/*
18832  * Based on:
18833  * Ext JS Library 1.1.1
18834  * Copyright(c) 2006-2007, Ext JS, LLC.
18835  *
18836  * Originally Released Under LGPL - original licence link has changed is not relivant.
18837  *
18838  * Fork - LGPL
18839  * <script type="text/javascript">
18840  */
18841
18842
18843
18844 /**
18845  * @class Roo.data.Store
18846  * @extends Roo.util.Observable
18847  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18848  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18849  * <p>
18850  * 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
18851  * has no knowledge of the format of the data returned by the Proxy.<br>
18852  * <p>
18853  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18854  * instances from the data object. These records are cached and made available through accessor functions.
18855  * @constructor
18856  * Creates a new Store.
18857  * @param {Object} config A config object containing the objects needed for the Store to access data,
18858  * and read the data into Records.
18859  */
18860 Roo.data.Store = function(config){
18861     this.data = new Roo.util.MixedCollection(false);
18862     this.data.getKey = function(o){
18863         return o.id;
18864     };
18865     this.baseParams = {};
18866     // private
18867     this.paramNames = {
18868         "start" : "start",
18869         "limit" : "limit",
18870         "sort" : "sort",
18871         "dir" : "dir"
18872     };
18873
18874     if(config && config.data){
18875         this.inlineData = config.data;
18876         delete config.data;
18877     }
18878
18879     Roo.apply(this, config);
18880     
18881     if(this.reader){ // reader passed
18882         this.reader = Roo.factory(this.reader, Roo.data);
18883         this.reader.xmodule = this.xmodule || false;
18884         if(!this.recordType){
18885             this.recordType = this.reader.recordType;
18886         }
18887         if(this.reader.onMetaChange){
18888             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18889         }
18890     }
18891
18892     if(this.recordType){
18893         this.fields = this.recordType.prototype.fields;
18894     }
18895     this.modified = [];
18896
18897     this.addEvents({
18898         /**
18899          * @event datachanged
18900          * Fires when the data cache has changed, and a widget which is using this Store
18901          * as a Record cache should refresh its view.
18902          * @param {Store} this
18903          */
18904         datachanged : true,
18905         /**
18906          * @event metachange
18907          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18908          * @param {Store} this
18909          * @param {Object} meta The JSON metadata
18910          */
18911         metachange : true,
18912         /**
18913          * @event add
18914          * Fires when Records have been added to the Store
18915          * @param {Store} this
18916          * @param {Roo.data.Record[]} records The array of Records added
18917          * @param {Number} index The index at which the record(s) were added
18918          */
18919         add : true,
18920         /**
18921          * @event remove
18922          * Fires when a Record has been removed from the Store
18923          * @param {Store} this
18924          * @param {Roo.data.Record} record The Record that was removed
18925          * @param {Number} index The index at which the record was removed
18926          */
18927         remove : true,
18928         /**
18929          * @event update
18930          * Fires when a Record has been updated
18931          * @param {Store} this
18932          * @param {Roo.data.Record} record The Record that was updated
18933          * @param {String} operation The update operation being performed.  Value may be one of:
18934          * <pre><code>
18935  Roo.data.Record.EDIT
18936  Roo.data.Record.REJECT
18937  Roo.data.Record.COMMIT
18938          * </code></pre>
18939          */
18940         update : true,
18941         /**
18942          * @event clear
18943          * Fires when the data cache has been cleared.
18944          * @param {Store} this
18945          */
18946         clear : true,
18947         /**
18948          * @event beforeload
18949          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18950          * the load action will be canceled.
18951          * @param {Store} this
18952          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18953          */
18954         beforeload : true,
18955         /**
18956          * @event load
18957          * Fires after a new set of Records has been loaded.
18958          * @param {Store} this
18959          * @param {Roo.data.Record[]} records The Records that were loaded
18960          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18961          */
18962         load : true,
18963         /**
18964          * @event loadexception
18965          * Fires if an exception occurs in the Proxy during loading.
18966          * Called with the signature of the Proxy's "loadexception" event.
18967          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
18968          * 
18969          * @param {Proxy} 
18970          * @param {Object} return from JsonData.reader() - success, totalRecords, records
18971          * @param {Object} load options 
18972          * @param {Object} jsonData from your request (normally this contains the Exception)
18973          */
18974         loadexception : true
18975     });
18976     
18977     if(this.proxy){
18978         this.proxy = Roo.factory(this.proxy, Roo.data);
18979         this.proxy.xmodule = this.xmodule || false;
18980         this.relayEvents(this.proxy,  ["loadexception"]);
18981     }
18982     this.sortToggle = {};
18983
18984     Roo.data.Store.superclass.constructor.call(this);
18985
18986     if(this.inlineData){
18987         this.loadData(this.inlineData);
18988         delete this.inlineData;
18989     }
18990 };
18991 Roo.extend(Roo.data.Store, Roo.util.Observable, {
18992      /**
18993     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
18994     * without a remote query - used by combo/forms at present.
18995     */
18996     
18997     /**
18998     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
18999     */
19000     /**
19001     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19002     */
19003     /**
19004     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19005     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19006     */
19007     /**
19008     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19009     * on any HTTP request
19010     */
19011     /**
19012     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19013     */
19014     /**
19015     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19016     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19017     */
19018     remoteSort : false,
19019
19020     /**
19021     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19022      * loaded or when a record is removed. (defaults to false).
19023     */
19024     pruneModifiedRecords : false,
19025
19026     // private
19027     lastOptions : null,
19028
19029     /**
19030      * Add Records to the Store and fires the add event.
19031      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19032      */
19033     add : function(records){
19034         records = [].concat(records);
19035         for(var i = 0, len = records.length; i < len; i++){
19036             records[i].join(this);
19037         }
19038         var index = this.data.length;
19039         this.data.addAll(records);
19040         this.fireEvent("add", this, records, index);
19041     },
19042
19043     /**
19044      * Remove a Record from the Store and fires the remove event.
19045      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19046      */
19047     remove : function(record){
19048         var index = this.data.indexOf(record);
19049         this.data.removeAt(index);
19050         if(this.pruneModifiedRecords){
19051             this.modified.remove(record);
19052         }
19053         this.fireEvent("remove", this, record, index);
19054     },
19055
19056     /**
19057      * Remove all Records from the Store and fires the clear event.
19058      */
19059     removeAll : function(){
19060         this.data.clear();
19061         if(this.pruneModifiedRecords){
19062             this.modified = [];
19063         }
19064         this.fireEvent("clear", this);
19065     },
19066
19067     /**
19068      * Inserts Records to the Store at the given index and fires the add event.
19069      * @param {Number} index The start index at which to insert the passed Records.
19070      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19071      */
19072     insert : function(index, records){
19073         records = [].concat(records);
19074         for(var i = 0, len = records.length; i < len; i++){
19075             this.data.insert(index, records[i]);
19076             records[i].join(this);
19077         }
19078         this.fireEvent("add", this, records, index);
19079     },
19080
19081     /**
19082      * Get the index within the cache of the passed Record.
19083      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19084      * @return {Number} The index of the passed Record. Returns -1 if not found.
19085      */
19086     indexOf : function(record){
19087         return this.data.indexOf(record);
19088     },
19089
19090     /**
19091      * Get the index within the cache of the Record with the passed id.
19092      * @param {String} id The id of the Record to find.
19093      * @return {Number} The index of the Record. Returns -1 if not found.
19094      */
19095     indexOfId : function(id){
19096         return this.data.indexOfKey(id);
19097     },
19098
19099     /**
19100      * Get the Record with the specified id.
19101      * @param {String} id The id of the Record to find.
19102      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19103      */
19104     getById : function(id){
19105         return this.data.key(id);
19106     },
19107
19108     /**
19109      * Get the Record at the specified index.
19110      * @param {Number} index The index of the Record to find.
19111      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19112      */
19113     getAt : function(index){
19114         return this.data.itemAt(index);
19115     },
19116
19117     /**
19118      * Returns a range of Records between specified indices.
19119      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19120      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19121      * @return {Roo.data.Record[]} An array of Records
19122      */
19123     getRange : function(start, end){
19124         return this.data.getRange(start, end);
19125     },
19126
19127     // private
19128     storeOptions : function(o){
19129         o = Roo.apply({}, o);
19130         delete o.callback;
19131         delete o.scope;
19132         this.lastOptions = o;
19133     },
19134
19135     /**
19136      * Loads the Record cache from the configured Proxy using the configured Reader.
19137      * <p>
19138      * If using remote paging, then the first load call must specify the <em>start</em>
19139      * and <em>limit</em> properties in the options.params property to establish the initial
19140      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19141      * <p>
19142      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19143      * and this call will return before the new data has been loaded. Perform any post-processing
19144      * in a callback function, or in a "load" event handler.</strong>
19145      * <p>
19146      * @param {Object} options An object containing properties which control loading options:<ul>
19147      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19148      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19149      * passed the following arguments:<ul>
19150      * <li>r : Roo.data.Record[]</li>
19151      * <li>options: Options object from the load call</li>
19152      * <li>success: Boolean success indicator</li></ul></li>
19153      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19154      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19155      * </ul>
19156      */
19157     load : function(options){
19158         options = options || {};
19159         if(this.fireEvent("beforeload", this, options) !== false){
19160             this.storeOptions(options);
19161             var p = Roo.apply(options.params || {}, this.baseParams);
19162             if(this.sortInfo && this.remoteSort){
19163                 var pn = this.paramNames;
19164                 p[pn["sort"]] = this.sortInfo.field;
19165                 p[pn["dir"]] = this.sortInfo.direction;
19166             }
19167             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19168         }
19169     },
19170
19171     /**
19172      * Reloads the Record cache from the configured Proxy using the configured Reader and
19173      * the options from the last load operation performed.
19174      * @param {Object} options (optional) An object containing properties which may override the options
19175      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19176      * the most recently used options are reused).
19177      */
19178     reload : function(options){
19179         this.load(Roo.applyIf(options||{}, this.lastOptions));
19180     },
19181
19182     // private
19183     // Called as a callback by the Reader during a load operation.
19184     loadRecords : function(o, options, success){
19185         if(!o || success === false){
19186             if(success !== false){
19187                 this.fireEvent("load", this, [], options);
19188             }
19189             if(options.callback){
19190                 options.callback.call(options.scope || this, [], options, false);
19191             }
19192             return;
19193         }
19194         // if data returned failure - throw an exception.
19195         if (o.success === false) {
19196             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19197             return;
19198         }
19199         var r = o.records, t = o.totalRecords || r.length;
19200         if(!options || options.add !== true){
19201             if(this.pruneModifiedRecords){
19202                 this.modified = [];
19203             }
19204             for(var i = 0, len = r.length; i < len; i++){
19205                 r[i].join(this);
19206             }
19207             if(this.snapshot){
19208                 this.data = this.snapshot;
19209                 delete this.snapshot;
19210             }
19211             this.data.clear();
19212             this.data.addAll(r);
19213             this.totalLength = t;
19214             this.applySort();
19215             this.fireEvent("datachanged", this);
19216         }else{
19217             this.totalLength = Math.max(t, this.data.length+r.length);
19218             this.add(r);
19219         }
19220         this.fireEvent("load", this, r, options);
19221         if(options.callback){
19222             options.callback.call(options.scope || this, r, options, true);
19223         }
19224     },
19225
19226     /**
19227      * Loads data from a passed data block. A Reader which understands the format of the data
19228      * must have been configured in the constructor.
19229      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19230      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19231      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19232      */
19233     loadData : function(o, append){
19234         var r = this.reader.readRecords(o);
19235         this.loadRecords(r, {add: append}, true);
19236     },
19237
19238     /**
19239      * Gets the number of cached records.
19240      * <p>
19241      * <em>If using paging, this may not be the total size of the dataset. If the data object
19242      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19243      * the data set size</em>
19244      */
19245     getCount : function(){
19246         return this.data.length || 0;
19247     },
19248
19249     /**
19250      * Gets the total number of records in the dataset as returned by the server.
19251      * <p>
19252      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19253      * the dataset size</em>
19254      */
19255     getTotalCount : function(){
19256         return this.totalLength || 0;
19257     },
19258
19259     /**
19260      * Returns the sort state of the Store as an object with two properties:
19261      * <pre><code>
19262  field {String} The name of the field by which the Records are sorted
19263  direction {String} The sort order, "ASC" or "DESC"
19264      * </code></pre>
19265      */
19266     getSortState : function(){
19267         return this.sortInfo;
19268     },
19269
19270     // private
19271     applySort : function(){
19272         if(this.sortInfo && !this.remoteSort){
19273             var s = this.sortInfo, f = s.field;
19274             var st = this.fields.get(f).sortType;
19275             var fn = function(r1, r2){
19276                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19277                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19278             };
19279             this.data.sort(s.direction, fn);
19280             if(this.snapshot && this.snapshot != this.data){
19281                 this.snapshot.sort(s.direction, fn);
19282             }
19283         }
19284     },
19285
19286     /**
19287      * Sets the default sort column and order to be used by the next load operation.
19288      * @param {String} fieldName The name of the field to sort by.
19289      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19290      */
19291     setDefaultSort : function(field, dir){
19292         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19293     },
19294
19295     /**
19296      * Sort the Records.
19297      * If remote sorting is used, the sort is performed on the server, and the cache is
19298      * reloaded. If local sorting is used, the cache is sorted internally.
19299      * @param {String} fieldName The name of the field to sort by.
19300      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19301      */
19302     sort : function(fieldName, dir){
19303         var f = this.fields.get(fieldName);
19304         if(!dir){
19305             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19306                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19307             }else{
19308                 dir = f.sortDir;
19309             }
19310         }
19311         this.sortToggle[f.name] = dir;
19312         this.sortInfo = {field: f.name, direction: dir};
19313         if(!this.remoteSort){
19314             this.applySort();
19315             this.fireEvent("datachanged", this);
19316         }else{
19317             this.load(this.lastOptions);
19318         }
19319     },
19320
19321     /**
19322      * Calls the specified function for each of the Records in the cache.
19323      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19324      * Returning <em>false</em> aborts and exits the iteration.
19325      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19326      */
19327     each : function(fn, scope){
19328         this.data.each(fn, scope);
19329     },
19330
19331     /**
19332      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19333      * (e.g., during paging).
19334      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19335      */
19336     getModifiedRecords : function(){
19337         return this.modified;
19338     },
19339
19340     // private
19341     createFilterFn : function(property, value, anyMatch){
19342         if(!value.exec){ // not a regex
19343             value = String(value);
19344             if(value.length == 0){
19345                 return false;
19346             }
19347             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19348         }
19349         return function(r){
19350             return value.test(r.data[property]);
19351         };
19352     },
19353
19354     /**
19355      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19356      * @param {String} property A field on your records
19357      * @param {Number} start The record index to start at (defaults to 0)
19358      * @param {Number} end The last record index to include (defaults to length - 1)
19359      * @return {Number} The sum
19360      */
19361     sum : function(property, start, end){
19362         var rs = this.data.items, v = 0;
19363         start = start || 0;
19364         end = (end || end === 0) ? end : rs.length-1;
19365
19366         for(var i = start; i <= end; i++){
19367             v += (rs[i].data[property] || 0);
19368         }
19369         return v;
19370     },
19371
19372     /**
19373      * Filter the records by a specified property.
19374      * @param {String} field A field on your records
19375      * @param {String/RegExp} value Either a string that the field
19376      * should start with or a RegExp to test against the field
19377      * @param {Boolean} anyMatch True to match any part not just the beginning
19378      */
19379     filter : function(property, value, anyMatch){
19380         var fn = this.createFilterFn(property, value, anyMatch);
19381         return fn ? this.filterBy(fn) : this.clearFilter();
19382     },
19383
19384     /**
19385      * Filter by a function. The specified function will be called with each
19386      * record in this data source. If the function returns true the record is included,
19387      * otherwise it is filtered.
19388      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19389      * @param {Object} scope (optional) The scope of the function (defaults to this)
19390      */
19391     filterBy : function(fn, scope){
19392         this.snapshot = this.snapshot || this.data;
19393         this.data = this.queryBy(fn, scope||this);
19394         this.fireEvent("datachanged", this);
19395     },
19396
19397     /**
19398      * Query the records by a specified property.
19399      * @param {String} field A field on your records
19400      * @param {String/RegExp} value Either a string that the field
19401      * should start with or a RegExp to test against the field
19402      * @param {Boolean} anyMatch True to match any part not just the beginning
19403      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19404      */
19405     query : function(property, value, anyMatch){
19406         var fn = this.createFilterFn(property, value, anyMatch);
19407         return fn ? this.queryBy(fn) : this.data.clone();
19408     },
19409
19410     /**
19411      * Query by a function. The specified function will be called with each
19412      * record in this data source. If the function returns true the record is included
19413      * in the results.
19414      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19415      * @param {Object} scope (optional) The scope of the function (defaults to this)
19416       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19417      **/
19418     queryBy : function(fn, scope){
19419         var data = this.snapshot || this.data;
19420         return data.filterBy(fn, scope||this);
19421     },
19422
19423     /**
19424      * Collects unique values for a particular dataIndex from this store.
19425      * @param {String} dataIndex The property to collect
19426      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19427      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19428      * @return {Array} An array of the unique values
19429      **/
19430     collect : function(dataIndex, allowNull, bypassFilter){
19431         var d = (bypassFilter === true && this.snapshot) ?
19432                 this.snapshot.items : this.data.items;
19433         var v, sv, r = [], l = {};
19434         for(var i = 0, len = d.length; i < len; i++){
19435             v = d[i].data[dataIndex];
19436             sv = String(v);
19437             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19438                 l[sv] = true;
19439                 r[r.length] = v;
19440             }
19441         }
19442         return r;
19443     },
19444
19445     /**
19446      * Revert to a view of the Record cache with no filtering applied.
19447      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19448      */
19449     clearFilter : function(suppressEvent){
19450         if(this.snapshot && this.snapshot != this.data){
19451             this.data = this.snapshot;
19452             delete this.snapshot;
19453             if(suppressEvent !== true){
19454                 this.fireEvent("datachanged", this);
19455             }
19456         }
19457     },
19458
19459     // private
19460     afterEdit : function(record){
19461         if(this.modified.indexOf(record) == -1){
19462             this.modified.push(record);
19463         }
19464         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19465     },
19466
19467     // private
19468     afterReject : function(record){
19469         this.modified.remove(record);
19470         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19471     },
19472
19473     // private
19474     afterCommit : function(record){
19475         this.modified.remove(record);
19476         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19477     },
19478
19479     /**
19480      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19481      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19482      */
19483     commitChanges : function(){
19484         var m = this.modified.slice(0);
19485         this.modified = [];
19486         for(var i = 0, len = m.length; i < len; i++){
19487             m[i].commit();
19488         }
19489     },
19490
19491     /**
19492      * Cancel outstanding changes on all changed records.
19493      */
19494     rejectChanges : function(){
19495         var m = this.modified.slice(0);
19496         this.modified = [];
19497         for(var i = 0, len = m.length; i < len; i++){
19498             m[i].reject();
19499         }
19500     },
19501
19502     onMetaChange : function(meta, rtype, o){
19503         this.recordType = rtype;
19504         this.fields = rtype.prototype.fields;
19505         delete this.snapshot;
19506         this.sortInfo = meta.sortInfo;
19507         this.modified = [];
19508         this.fireEvent('metachange', this, this.reader.meta);
19509     }
19510 });/*
19511  * Based on:
19512  * Ext JS Library 1.1.1
19513  * Copyright(c) 2006-2007, Ext JS, LLC.
19514  *
19515  * Originally Released Under LGPL - original licence link has changed is not relivant.
19516  *
19517  * Fork - LGPL
19518  * <script type="text/javascript">
19519  */
19520
19521 /**
19522  * @class Roo.data.SimpleStore
19523  * @extends Roo.data.Store
19524  * Small helper class to make creating Stores from Array data easier.
19525  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19526  * @cfg {Array} fields An array of field definition objects, or field name strings.
19527  * @cfg {Array} data The multi-dimensional array of data
19528  * @constructor
19529  * @param {Object} config
19530  */
19531 Roo.data.SimpleStore = function(config){
19532     Roo.data.SimpleStore.superclass.constructor.call(this, {
19533         isLocal : true,
19534         reader: new Roo.data.ArrayReader({
19535                 id: config.id
19536             },
19537             Roo.data.Record.create(config.fields)
19538         ),
19539         proxy : new Roo.data.MemoryProxy(config.data)
19540     });
19541     this.load();
19542 };
19543 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19544  * Based on:
19545  * Ext JS Library 1.1.1
19546  * Copyright(c) 2006-2007, Ext JS, LLC.
19547  *
19548  * Originally Released Under LGPL - original licence link has changed is not relivant.
19549  *
19550  * Fork - LGPL
19551  * <script type="text/javascript">
19552  */
19553
19554 /**
19555 /**
19556  * @extends Roo.data.Store
19557  * @class Roo.data.JsonStore
19558  * Small helper class to make creating Stores for JSON data easier. <br/>
19559 <pre><code>
19560 var store = new Roo.data.JsonStore({
19561     url: 'get-images.php',
19562     root: 'images',
19563     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19564 });
19565 </code></pre>
19566  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19567  * JsonReader and HttpProxy (unless inline data is provided).</b>
19568  * @cfg {Array} fields An array of field definition objects, or field name strings.
19569  * @constructor
19570  * @param {Object} config
19571  */
19572 Roo.data.JsonStore = function(c){
19573     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19574         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19575         reader: new Roo.data.JsonReader(c, c.fields)
19576     }));
19577 };
19578 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19579  * Based on:
19580  * Ext JS Library 1.1.1
19581  * Copyright(c) 2006-2007, Ext JS, LLC.
19582  *
19583  * Originally Released Under LGPL - original licence link has changed is not relivant.
19584  *
19585  * Fork - LGPL
19586  * <script type="text/javascript">
19587  */
19588
19589  
19590 Roo.data.Field = function(config){
19591     if(typeof config == "string"){
19592         config = {name: config};
19593     }
19594     Roo.apply(this, config);
19595     
19596     if(!this.type){
19597         this.type = "auto";
19598     }
19599     
19600     var st = Roo.data.SortTypes;
19601     // named sortTypes are supported, here we look them up
19602     if(typeof this.sortType == "string"){
19603         this.sortType = st[this.sortType];
19604     }
19605     
19606     // set default sortType for strings and dates
19607     if(!this.sortType){
19608         switch(this.type){
19609             case "string":
19610                 this.sortType = st.asUCString;
19611                 break;
19612             case "date":
19613                 this.sortType = st.asDate;
19614                 break;
19615             default:
19616                 this.sortType = st.none;
19617         }
19618     }
19619
19620     // define once
19621     var stripRe = /[\$,%]/g;
19622
19623     // prebuilt conversion function for this field, instead of
19624     // switching every time we're reading a value
19625     if(!this.convert){
19626         var cv, dateFormat = this.dateFormat;
19627         switch(this.type){
19628             case "":
19629             case "auto":
19630             case undefined:
19631                 cv = function(v){ return v; };
19632                 break;
19633             case "string":
19634                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19635                 break;
19636             case "int":
19637                 cv = function(v){
19638                     return v !== undefined && v !== null && v !== '' ?
19639                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19640                     };
19641                 break;
19642             case "float":
19643                 cv = function(v){
19644                     return v !== undefined && v !== null && v !== '' ?
19645                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19646                     };
19647                 break;
19648             case "bool":
19649             case "boolean":
19650                 cv = function(v){ return v === true || v === "true" || v == 1; };
19651                 break;
19652             case "date":
19653                 cv = function(v){
19654                     if(!v){
19655                         return '';
19656                     }
19657                     if(v instanceof Date){
19658                         return v;
19659                     }
19660                     if(dateFormat){
19661                         if(dateFormat == "timestamp"){
19662                             return new Date(v*1000);
19663                         }
19664                         return Date.parseDate(v, dateFormat);
19665                     }
19666                     var parsed = Date.parse(v);
19667                     return parsed ? new Date(parsed) : null;
19668                 };
19669              break;
19670             
19671         }
19672         this.convert = cv;
19673     }
19674 };
19675
19676 Roo.data.Field.prototype = {
19677     dateFormat: null,
19678     defaultValue: "",
19679     mapping: null,
19680     sortType : null,
19681     sortDir : "ASC"
19682 };/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692  
19693 // Base class for reading structured data from a data source.  This class is intended to be
19694 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19695
19696 /**
19697  * @class Roo.data.DataReader
19698  * Base class for reading structured data from a data source.  This class is intended to be
19699  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19700  */
19701
19702 Roo.data.DataReader = function(meta, recordType){
19703     
19704     this.meta = meta;
19705     
19706     this.recordType = recordType instanceof Array ? 
19707         Roo.data.Record.create(recordType) : recordType;
19708 };
19709
19710 Roo.data.DataReader.prototype = {
19711      /**
19712      * Create an empty record
19713      * @param {Object} data (optional) - overlay some values
19714      * @return {Roo.data.Record} record created.
19715      */
19716     newRow :  function(d) {
19717         var da =  {};
19718         this.recordType.prototype.fields.each(function(c) {
19719             switch( c.type) {
19720                 case 'int' : da[c.name] = 0; break;
19721                 case 'date' : da[c.name] = new Date(); break;
19722                 case 'float' : da[c.name] = 0.0; break;
19723                 case 'boolean' : da[c.name] = false; break;
19724                 default : da[c.name] = ""; break;
19725             }
19726             
19727         });
19728         return new this.recordType(Roo.apply(da, d));
19729     }
19730     
19731 };/*
19732  * Based on:
19733  * Ext JS Library 1.1.1
19734  * Copyright(c) 2006-2007, Ext JS, LLC.
19735  *
19736  * Originally Released Under LGPL - original licence link has changed is not relivant.
19737  *
19738  * Fork - LGPL
19739  * <script type="text/javascript">
19740  */
19741
19742 /**
19743  * @class Roo.data.DataProxy
19744  * @extends Roo.data.Observable
19745  * This class is an abstract base class for implementations which provide retrieval of
19746  * unformatted data objects.<br>
19747  * <p>
19748  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19749  * (of the appropriate type which knows how to parse the data object) to provide a block of
19750  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19751  * <p>
19752  * Custom implementations must implement the load method as described in
19753  * {@link Roo.data.HttpProxy#load}.
19754  */
19755 Roo.data.DataProxy = function(){
19756     this.addEvents({
19757         /**
19758          * @event beforeload
19759          * Fires before a network request is made to retrieve a data object.
19760          * @param {Object} This DataProxy object.
19761          * @param {Object} params The params parameter to the load function.
19762          */
19763         beforeload : true,
19764         /**
19765          * @event load
19766          * Fires before the load method's callback is called.
19767          * @param {Object} This DataProxy object.
19768          * @param {Object} o The data object.
19769          * @param {Object} arg The callback argument object passed to the load function.
19770          */
19771         load : true,
19772         /**
19773          * @event loadexception
19774          * Fires if an Exception occurs during data retrieval.
19775          * @param {Object} This DataProxy object.
19776          * @param {Object} o The data object.
19777          * @param {Object} arg The callback argument object passed to the load function.
19778          * @param {Object} e The Exception.
19779          */
19780         loadexception : true
19781     });
19782     Roo.data.DataProxy.superclass.constructor.call(this);
19783 };
19784
19785 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19786
19787     /**
19788      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19789      */
19790 /*
19791  * Based on:
19792  * Ext JS Library 1.1.1
19793  * Copyright(c) 2006-2007, Ext JS, LLC.
19794  *
19795  * Originally Released Under LGPL - original licence link has changed is not relivant.
19796  *
19797  * Fork - LGPL
19798  * <script type="text/javascript">
19799  */
19800 /**
19801  * @class Roo.data.MemoryProxy
19802  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19803  * to the Reader when its load method is called.
19804  * @constructor
19805  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19806  */
19807 Roo.data.MemoryProxy = function(data){
19808     if (data.data) {
19809         data = data.data;
19810     }
19811     Roo.data.MemoryProxy.superclass.constructor.call(this);
19812     this.data = data;
19813 };
19814
19815 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19816     /**
19817      * Load data from the requested source (in this case an in-memory
19818      * data object passed to the constructor), read the data object into
19819      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19820      * process that block using the passed callback.
19821      * @param {Object} params This parameter is not used by the MemoryProxy class.
19822      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19823      * object into a block of Roo.data.Records.
19824      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19825      * The function must be passed <ul>
19826      * <li>The Record block object</li>
19827      * <li>The "arg" argument from the load function</li>
19828      * <li>A boolean success indicator</li>
19829      * </ul>
19830      * @param {Object} scope The scope in which to call the callback
19831      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19832      */
19833     load : function(params, reader, callback, scope, arg){
19834         params = params || {};
19835         var result;
19836         try {
19837             result = reader.readRecords(this.data);
19838         }catch(e){
19839             this.fireEvent("loadexception", this, arg, null, e);
19840             callback.call(scope, null, arg, false);
19841             return;
19842         }
19843         callback.call(scope, result, arg, true);
19844     },
19845     
19846     // private
19847     update : function(params, records){
19848         
19849     }
19850 });/*
19851  * Based on:
19852  * Ext JS Library 1.1.1
19853  * Copyright(c) 2006-2007, Ext JS, LLC.
19854  *
19855  * Originally Released Under LGPL - original licence link has changed is not relivant.
19856  *
19857  * Fork - LGPL
19858  * <script type="text/javascript">
19859  */
19860 /**
19861  * @class Roo.data.HttpProxy
19862  * @extends Roo.data.DataProxy
19863  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19864  * configured to reference a certain URL.<br><br>
19865  * <p>
19866  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19867  * from which the running page was served.<br><br>
19868  * <p>
19869  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19870  * <p>
19871  * Be aware that to enable the browser to parse an XML document, the server must set
19872  * the Content-Type header in the HTTP response to "text/xml".
19873  * @constructor
19874  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19875  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19876  * will be used to make the request.
19877  */
19878 Roo.data.HttpProxy = function(conn){
19879     Roo.data.HttpProxy.superclass.constructor.call(this);
19880     // is conn a conn config or a real conn?
19881     this.conn = conn;
19882     this.useAjax = !conn || !conn.events;
19883   
19884 };
19885
19886 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19887     // thse are take from connection...
19888     
19889     /**
19890      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19891      */
19892     /**
19893      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19894      * extra parameters to each request made by this object. (defaults to undefined)
19895      */
19896     /**
19897      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19898      *  to each request made by this object. (defaults to undefined)
19899      */
19900     /**
19901      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
19902      */
19903     /**
19904      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19905      */
19906      /**
19907      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19908      * @type Boolean
19909      */
19910   
19911
19912     /**
19913      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19914      * @type Boolean
19915      */
19916     /**
19917      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19918      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19919      * a finer-grained basis than the DataProxy events.
19920      */
19921     getConnection : function(){
19922         return this.useAjax ? Roo.Ajax : this.conn;
19923     },
19924
19925     /**
19926      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19927      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19928      * process that block using the passed callback.
19929      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19930      * for the request to the remote server.
19931      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19932      * object into a block of Roo.data.Records.
19933      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19934      * The function must be passed <ul>
19935      * <li>The Record block object</li>
19936      * <li>The "arg" argument from the load function</li>
19937      * <li>A boolean success indicator</li>
19938      * </ul>
19939      * @param {Object} scope The scope in which to call the callback
19940      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19941      */
19942     load : function(params, reader, callback, scope, arg){
19943         if(this.fireEvent("beforeload", this, params) !== false){
19944             var  o = {
19945                 params : params || {},
19946                 request: {
19947                     callback : callback,
19948                     scope : scope,
19949                     arg : arg
19950                 },
19951                 reader: reader,
19952                 callback : this.loadResponse,
19953                 scope: this
19954             };
19955             if(this.useAjax){
19956                 Roo.applyIf(o, this.conn);
19957                 if(this.activeRequest){
19958                     Roo.Ajax.abort(this.activeRequest);
19959                 }
19960                 this.activeRequest = Roo.Ajax.request(o);
19961             }else{
19962                 this.conn.request(o);
19963             }
19964         }else{
19965             callback.call(scope||this, null, arg, false);
19966         }
19967     },
19968
19969     // private
19970     loadResponse : function(o, success, response){
19971         delete this.activeRequest;
19972         if(!success){
19973             this.fireEvent("loadexception", this, o, response);
19974             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19975             return;
19976         }
19977         var result;
19978         try {
19979             result = o.reader.read(response);
19980         }catch(e){
19981             this.fireEvent("loadexception", this, o, response, e);
19982             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19983             return;
19984         }
19985         
19986         this.fireEvent("load", this, o, o.request.arg);
19987         o.request.callback.call(o.request.scope, result, o.request.arg, true);
19988     },
19989
19990     // private
19991     update : function(dataSet){
19992
19993     },
19994
19995     // private
19996     updateResponse : function(dataSet){
19997
19998     }
19999 });/*
20000  * Based on:
20001  * Ext JS Library 1.1.1
20002  * Copyright(c) 2006-2007, Ext JS, LLC.
20003  *
20004  * Originally Released Under LGPL - original licence link has changed is not relivant.
20005  *
20006  * Fork - LGPL
20007  * <script type="text/javascript">
20008  */
20009
20010 /**
20011  * @class Roo.data.ScriptTagProxy
20012  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20013  * other than the originating domain of the running page.<br><br>
20014  * <p>
20015  * <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
20016  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20017  * <p>
20018  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20019  * source code that is used as the source inside a &lt;script> tag.<br><br>
20020  * <p>
20021  * In order for the browser to process the returned data, the server must wrap the data object
20022  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20023  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20024  * depending on whether the callback name was passed:
20025  * <p>
20026  * <pre><code>
20027 boolean scriptTag = false;
20028 String cb = request.getParameter("callback");
20029 if (cb != null) {
20030     scriptTag = true;
20031     response.setContentType("text/javascript");
20032 } else {
20033     response.setContentType("application/x-json");
20034 }
20035 Writer out = response.getWriter();
20036 if (scriptTag) {
20037     out.write(cb + "(");
20038 }
20039 out.print(dataBlock.toJsonString());
20040 if (scriptTag) {
20041     out.write(");");
20042 }
20043 </pre></code>
20044  *
20045  * @constructor
20046  * @param {Object} config A configuration object.
20047  */
20048 Roo.data.ScriptTagProxy = function(config){
20049     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20050     Roo.apply(this, config);
20051     this.head = document.getElementsByTagName("head")[0];
20052 };
20053
20054 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20055
20056 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20057     /**
20058      * @cfg {String} url The URL from which to request the data object.
20059      */
20060     /**
20061      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20062      */
20063     timeout : 30000,
20064     /**
20065      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20066      * the server the name of the callback function set up by the load call to process the returned data object.
20067      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20068      * javascript output which calls this named function passing the data object as its only parameter.
20069      */
20070     callbackParam : "callback",
20071     /**
20072      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20073      * name to the request.
20074      */
20075     nocache : true,
20076
20077     /**
20078      * Load data from the configured URL, read the data object into
20079      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20080      * process that block using the passed callback.
20081      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20082      * for the request to the remote server.
20083      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20084      * object into a block of Roo.data.Records.
20085      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20086      * The function must be passed <ul>
20087      * <li>The Record block object</li>
20088      * <li>The "arg" argument from the load function</li>
20089      * <li>A boolean success indicator</li>
20090      * </ul>
20091      * @param {Object} scope The scope in which to call the callback
20092      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20093      */
20094     load : function(params, reader, callback, scope, arg){
20095         if(this.fireEvent("beforeload", this, params) !== false){
20096
20097             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20098
20099             var url = this.url;
20100             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20101             if(this.nocache){
20102                 url += "&_dc=" + (new Date().getTime());
20103             }
20104             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20105             var trans = {
20106                 id : transId,
20107                 cb : "stcCallback"+transId,
20108                 scriptId : "stcScript"+transId,
20109                 params : params,
20110                 arg : arg,
20111                 url : url,
20112                 callback : callback,
20113                 scope : scope,
20114                 reader : reader
20115             };
20116             var conn = this;
20117
20118             window[trans.cb] = function(o){
20119                 conn.handleResponse(o, trans);
20120             };
20121
20122             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20123
20124             if(this.autoAbort !== false){
20125                 this.abort();
20126             }
20127
20128             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20129
20130             var script = document.createElement("script");
20131             script.setAttribute("src", url);
20132             script.setAttribute("type", "text/javascript");
20133             script.setAttribute("id", trans.scriptId);
20134             this.head.appendChild(script);
20135
20136             this.trans = trans;
20137         }else{
20138             callback.call(scope||this, null, arg, false);
20139         }
20140     },
20141
20142     // private
20143     isLoading : function(){
20144         return this.trans ? true : false;
20145     },
20146
20147     /**
20148      * Abort the current server request.
20149      */
20150     abort : function(){
20151         if(this.isLoading()){
20152             this.destroyTrans(this.trans);
20153         }
20154     },
20155
20156     // private
20157     destroyTrans : function(trans, isLoaded){
20158         this.head.removeChild(document.getElementById(trans.scriptId));
20159         clearTimeout(trans.timeoutId);
20160         if(isLoaded){
20161             window[trans.cb] = undefined;
20162             try{
20163                 delete window[trans.cb];
20164             }catch(e){}
20165         }else{
20166             // if hasn't been loaded, wait for load to remove it to prevent script error
20167             window[trans.cb] = function(){
20168                 window[trans.cb] = undefined;
20169                 try{
20170                     delete window[trans.cb];
20171                 }catch(e){}
20172             };
20173         }
20174     },
20175
20176     // private
20177     handleResponse : function(o, trans){
20178         this.trans = false;
20179         this.destroyTrans(trans, true);
20180         var result;
20181         try {
20182             result = trans.reader.readRecords(o);
20183         }catch(e){
20184             this.fireEvent("loadexception", this, o, trans.arg, e);
20185             trans.callback.call(trans.scope||window, null, trans.arg, false);
20186             return;
20187         }
20188         this.fireEvent("load", this, o, trans.arg);
20189         trans.callback.call(trans.scope||window, result, trans.arg, true);
20190     },
20191
20192     // private
20193     handleFailure : function(trans){
20194         this.trans = false;
20195         this.destroyTrans(trans, false);
20196         this.fireEvent("loadexception", this, null, trans.arg);
20197         trans.callback.call(trans.scope||window, null, trans.arg, false);
20198     }
20199 });/*
20200  * Based on:
20201  * Ext JS Library 1.1.1
20202  * Copyright(c) 2006-2007, Ext JS, LLC.
20203  *
20204  * Originally Released Under LGPL - original licence link has changed is not relivant.
20205  *
20206  * Fork - LGPL
20207  * <script type="text/javascript">
20208  */
20209
20210 /**
20211  * @class Roo.data.JsonReader
20212  * @extends Roo.data.DataReader
20213  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20214  * based on mappings in a provided Roo.data.Record constructor.
20215  * <p>
20216  * Example code:
20217  * <pre><code>
20218 var RecordDef = Roo.data.Record.create([
20219     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20220     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20221 ]);
20222 var myReader = new Roo.data.JsonReader({
20223     totalProperty: "results",    // The property which contains the total dataset size (optional)
20224     root: "rows",                // The property which contains an Array of row objects
20225     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20226 }, RecordDef);
20227 </code></pre>
20228  * <p>
20229  * This would consume a JSON file like this:
20230  * <pre><code>
20231 { 'results': 2, 'rows': [
20232     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20233     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20234 }
20235 </code></pre>
20236  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20237  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20238  * paged from the remote server.
20239  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20240  * @cfg {String} root name of the property which contains the Array of row objects.
20241  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20242  * @constructor
20243  * Create a new JsonReader
20244  * @param {Object} meta Metadata configuration options
20245  * @param {Object} recordType Either an Array of field definition objects,
20246  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20247  */
20248 Roo.data.JsonReader = function(meta, recordType){
20249     
20250     meta = meta || {};
20251     // set some defaults:
20252     Roo.applyIf(meta, {
20253         totalProperty: 'total',
20254         successProperty : 'success',
20255         root : 'data',
20256         id : 'id'
20257     });
20258     
20259     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20260 };
20261 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20262     /**
20263      * This method is only used by a DataProxy which has retrieved data from a remote server.
20264      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20265      * @return {Object} data A data block which is used by an Roo.data.Store object as
20266      * a cache of Roo.data.Records.
20267      */
20268     read : function(response){
20269         var json = response.responseText;
20270         /* eval:var:o */
20271         var o = eval("("+json+")");
20272         if(!o) {
20273             throw {message: "JsonReader.read: Json object not found"};
20274         }
20275         
20276         if(o.metaData){
20277             delete this.ef;
20278             this.meta = o.metaData;
20279             this.recordType = Roo.data.Record.create(o.metaData.fields);
20280             this.onMetaChange(this.meta, this.recordType, o);
20281         }
20282         return this.readRecords(o);
20283     },
20284
20285     // private function a store will implement
20286     onMetaChange : function(meta, recordType, o){
20287
20288     },
20289
20290     /**
20291          * @ignore
20292          */
20293     simpleAccess: function(obj, subsc) {
20294         return obj[subsc];
20295     },
20296
20297         /**
20298          * @ignore
20299          */
20300     getJsonAccessor: function(){
20301         var re = /[\[\.]/;
20302         return function(expr) {
20303             try {
20304                 return(re.test(expr))
20305                     ? new Function("obj", "return obj." + expr)
20306                     : function(obj){
20307                         return obj[expr];
20308                     };
20309             } catch(e){}
20310             return Roo.emptyFn;
20311         };
20312     }(),
20313
20314     /**
20315      * Create a data block containing Roo.data.Records from an XML document.
20316      * @param {Object} o An object which contains an Array of row objects in the property specified
20317      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20318      * which contains the total size of the dataset.
20319      * @return {Object} data A data block which is used by an Roo.data.Store object as
20320      * a cache of Roo.data.Records.
20321      */
20322     readRecords : function(o){
20323         /**
20324          * After any data loads, the raw JSON data is available for further custom processing.
20325          * @type Object
20326          */
20327         this.jsonData = o;
20328         var s = this.meta, Record = this.recordType,
20329             f = Record.prototype.fields, fi = f.items, fl = f.length;
20330
20331 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20332         if (!this.ef) {
20333             if(s.totalProperty) {
20334                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20335                 }
20336                 if(s.successProperty) {
20337                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20338                 }
20339                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20340                 if (s.id) {
20341                         var g = this.getJsonAccessor(s.id);
20342                         this.getId = function(rec) {
20343                                 var r = g(rec);
20344                                 return (r === undefined || r === "") ? null : r;
20345                         };
20346                 } else {
20347                         this.getId = function(){return null;};
20348                 }
20349             this.ef = [];
20350             for(var i = 0; i < fl; i++){
20351                 f = fi[i];
20352                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20353                 this.ef[i] = this.getJsonAccessor(map);
20354             }
20355         }
20356
20357         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20358         if(s.totalProperty){
20359             var v = parseInt(this.getTotal(o), 10);
20360             if(!isNaN(v)){
20361                 totalRecords = v;
20362             }
20363         }
20364         if(s.successProperty){
20365             var v = this.getSuccess(o);
20366             if(v === false || v === 'false'){
20367                 success = false;
20368             }
20369         }
20370         var records = [];
20371             for(var i = 0; i < c; i++){
20372                     var n = root[i];
20373                 var values = {};
20374                 var id = this.getId(n);
20375                 for(var j = 0; j < fl; j++){
20376                     f = fi[j];
20377                 var v = this.ef[j](n);
20378                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20379                 }
20380                 var record = new Record(values, id);
20381                 record.json = n;
20382                 records[i] = record;
20383             }
20384             return {
20385                 success : success,
20386                 records : records,
20387                 totalRecords : totalRecords
20388             };
20389     }
20390 });/*
20391  * Based on:
20392  * Ext JS Library 1.1.1
20393  * Copyright(c) 2006-2007, Ext JS, LLC.
20394  *
20395  * Originally Released Under LGPL - original licence link has changed is not relivant.
20396  *
20397  * Fork - LGPL
20398  * <script type="text/javascript">
20399  */
20400
20401 /**
20402  * @class Roo.data.XmlReader
20403  * @extends Roo.data.DataReader
20404  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20405  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20406  * <p>
20407  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20408  * header in the HTTP response must be set to "text/xml".</em>
20409  * <p>
20410  * Example code:
20411  * <pre><code>
20412 var RecordDef = Roo.data.Record.create([
20413    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20414    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20415 ]);
20416 var myReader = new Roo.data.XmlReader({
20417    totalRecords: "results", // The element which contains the total dataset size (optional)
20418    record: "row",           // The repeated element which contains row information
20419    id: "id"                 // The element within the row that provides an ID for the record (optional)
20420 }, RecordDef);
20421 </code></pre>
20422  * <p>
20423  * This would consume an XML file like this:
20424  * <pre><code>
20425 &lt;?xml?>
20426 &lt;dataset>
20427  &lt;results>2&lt;/results>
20428  &lt;row>
20429    &lt;id>1&lt;/id>
20430    &lt;name>Bill&lt;/name>
20431    &lt;occupation>Gardener&lt;/occupation>
20432  &lt;/row>
20433  &lt;row>
20434    &lt;id>2&lt;/id>
20435    &lt;name>Ben&lt;/name>
20436    &lt;occupation>Horticulturalist&lt;/occupation>
20437  &lt;/row>
20438 &lt;/dataset>
20439 </code></pre>
20440  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20441  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20442  * paged from the remote server.
20443  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20444  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20445  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20446  * a record identifier value.
20447  * @constructor
20448  * Create a new XmlReader
20449  * @param {Object} meta Metadata configuration options
20450  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20451  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20452  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20453  */
20454 Roo.data.XmlReader = function(meta, recordType){
20455     meta = meta || {};
20456     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20457 };
20458 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20459     /**
20460      * This method is only used by a DataProxy which has retrieved data from a remote server.
20461          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20462          * to contain a method called 'responseXML' that returns an XML document object.
20463      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20464      * a cache of Roo.data.Records.
20465      */
20466     read : function(response){
20467         var doc = response.responseXML;
20468         if(!doc) {
20469             throw {message: "XmlReader.read: XML Document not available"};
20470         }
20471         return this.readRecords(doc);
20472     },
20473
20474     /**
20475      * Create a data block containing Roo.data.Records from an XML document.
20476          * @param {Object} doc A parsed XML document.
20477      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20478      * a cache of Roo.data.Records.
20479      */
20480     readRecords : function(doc){
20481         /**
20482          * After any data loads/reads, the raw XML Document is available for further custom processing.
20483          * @type XMLDocument
20484          */
20485         this.xmlData = doc;
20486         var root = doc.documentElement || doc;
20487         var q = Roo.DomQuery;
20488         var recordType = this.recordType, fields = recordType.prototype.fields;
20489         var sid = this.meta.id;
20490         var totalRecords = 0, success = true;
20491         if(this.meta.totalRecords){
20492             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20493         }
20494         
20495         if(this.meta.success){
20496             var sv = q.selectValue(this.meta.success, root, true);
20497             success = sv !== false && sv !== 'false';
20498         }
20499         var records = [];
20500         var ns = q.select(this.meta.record, root);
20501         for(var i = 0, len = ns.length; i < len; i++) {
20502                 var n = ns[i];
20503                 var values = {};
20504                 var id = sid ? q.selectValue(sid, n) : undefined;
20505                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20506                     var f = fields.items[j];
20507                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20508                     v = f.convert(v);
20509                     values[f.name] = v;
20510                 }
20511                 var record = new recordType(values, id);
20512                 record.node = n;
20513                 records[records.length] = record;
20514             }
20515
20516             return {
20517                 success : success,
20518                 records : records,
20519                 totalRecords : totalRecords || records.length
20520             };
20521     }
20522 });/*
20523  * Based on:
20524  * Ext JS Library 1.1.1
20525  * Copyright(c) 2006-2007, Ext JS, LLC.
20526  *
20527  * Originally Released Under LGPL - original licence link has changed is not relivant.
20528  *
20529  * Fork - LGPL
20530  * <script type="text/javascript">
20531  */
20532
20533 /**
20534  * @class Roo.data.ArrayReader
20535  * @extends Roo.data.DataReader
20536  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20537  * Each element of that Array represents a row of data fields. The
20538  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20539  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20540  * <p>
20541  * Example code:.
20542  * <pre><code>
20543 var RecordDef = Roo.data.Record.create([
20544     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20545     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20546 ]);
20547 var myReader = new Roo.data.ArrayReader({
20548     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20549 }, RecordDef);
20550 </code></pre>
20551  * <p>
20552  * This would consume an Array like this:
20553  * <pre><code>
20554 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20555   </code></pre>
20556  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20557  * @constructor
20558  * Create a new JsonReader
20559  * @param {Object} meta Metadata configuration options.
20560  * @param {Object} recordType Either an Array of field definition objects
20561  * as specified to {@link Roo.data.Record#create},
20562  * or an {@link Roo.data.Record} object
20563  * created using {@link Roo.data.Record#create}.
20564  */
20565 Roo.data.ArrayReader = function(meta, recordType){
20566     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20567 };
20568
20569 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20570     /**
20571      * Create a data block containing Roo.data.Records from an XML document.
20572      * @param {Object} o An Array of row objects which represents the dataset.
20573      * @return {Object} data A data block which is used by an Roo.data.Store object as
20574      * a cache of Roo.data.Records.
20575      */
20576     readRecords : function(o){
20577         var sid = this.meta ? this.meta.id : null;
20578         var recordType = this.recordType, fields = recordType.prototype.fields;
20579         var records = [];
20580         var root = o;
20581             for(var i = 0; i < root.length; i++){
20582                     var n = root[i];
20583                 var values = {};
20584                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20585                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20586                 var f = fields.items[j];
20587                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20588                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20589                 v = f.convert(v);
20590                 values[f.name] = v;
20591             }
20592                 var record = new recordType(values, id);
20593                 record.json = n;
20594                 records[records.length] = record;
20595             }
20596             return {
20597                 records : records,
20598                 totalRecords : records.length
20599             };
20600     }
20601 });/*
20602  * Based on:
20603  * Ext JS Library 1.1.1
20604  * Copyright(c) 2006-2007, Ext JS, LLC.
20605  *
20606  * Originally Released Under LGPL - original licence link has changed is not relivant.
20607  *
20608  * Fork - LGPL
20609  * <script type="text/javascript">
20610  */
20611
20612
20613 /**
20614  * @class Roo.data.Tree
20615  * @extends Roo.util.Observable
20616  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20617  * in the tree have most standard DOM functionality.
20618  * @constructor
20619  * @param {Node} root (optional) The root node
20620  */
20621 Roo.data.Tree = function(root){
20622    this.nodeHash = {};
20623    /**
20624     * The root node for this tree
20625     * @type Node
20626     */
20627    this.root = null;
20628    if(root){
20629        this.setRootNode(root);
20630    }
20631    this.addEvents({
20632        /**
20633         * @event append
20634         * Fires when a new child node is appended to a node in this tree.
20635         * @param {Tree} tree The owner tree
20636         * @param {Node} parent The parent node
20637         * @param {Node} node The newly appended node
20638         * @param {Number} index The index of the newly appended node
20639         */
20640        "append" : true,
20641        /**
20642         * @event remove
20643         * Fires when a child node is removed from a node in this tree.
20644         * @param {Tree} tree The owner tree
20645         * @param {Node} parent The parent node
20646         * @param {Node} node The child node removed
20647         */
20648        "remove" : true,
20649        /**
20650         * @event move
20651         * Fires when a node is moved to a new location in the tree
20652         * @param {Tree} tree The owner tree
20653         * @param {Node} node The node moved
20654         * @param {Node} oldParent The old parent of this node
20655         * @param {Node} newParent The new parent of this node
20656         * @param {Number} index The index it was moved to
20657         */
20658        "move" : true,
20659        /**
20660         * @event insert
20661         * Fires when a new child node is inserted in a node in this tree.
20662         * @param {Tree} tree The owner tree
20663         * @param {Node} parent The parent node
20664         * @param {Node} node The child node inserted
20665         * @param {Node} refNode The child node the node was inserted before
20666         */
20667        "insert" : true,
20668        /**
20669         * @event beforeappend
20670         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20671         * @param {Tree} tree The owner tree
20672         * @param {Node} parent The parent node
20673         * @param {Node} node The child node to be appended
20674         */
20675        "beforeappend" : true,
20676        /**
20677         * @event beforeremove
20678         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20679         * @param {Tree} tree The owner tree
20680         * @param {Node} parent The parent node
20681         * @param {Node} node The child node to be removed
20682         */
20683        "beforeremove" : true,
20684        /**
20685         * @event beforemove
20686         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20687         * @param {Tree} tree The owner tree
20688         * @param {Node} node The node being moved
20689         * @param {Node} oldParent The parent of the node
20690         * @param {Node} newParent The new parent the node is moving to
20691         * @param {Number} index The index it is being moved to
20692         */
20693        "beforemove" : true,
20694        /**
20695         * @event beforeinsert
20696         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20697         * @param {Tree} tree The owner tree
20698         * @param {Node} parent The parent node
20699         * @param {Node} node The child node to be inserted
20700         * @param {Node} refNode The child node the node is being inserted before
20701         */
20702        "beforeinsert" : true
20703    });
20704
20705     Roo.data.Tree.superclass.constructor.call(this);
20706 };
20707
20708 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20709     pathSeparator: "/",
20710
20711     proxyNodeEvent : function(){
20712         return this.fireEvent.apply(this, arguments);
20713     },
20714
20715     /**
20716      * Returns the root node for this tree.
20717      * @return {Node}
20718      */
20719     getRootNode : function(){
20720         return this.root;
20721     },
20722
20723     /**
20724      * Sets the root node for this tree.
20725      * @param {Node} node
20726      * @return {Node}
20727      */
20728     setRootNode : function(node){
20729         this.root = node;
20730         node.ownerTree = this;
20731         node.isRoot = true;
20732         this.registerNode(node);
20733         return node;
20734     },
20735
20736     /**
20737      * Gets a node in this tree by its id.
20738      * @param {String} id
20739      * @return {Node}
20740      */
20741     getNodeById : function(id){
20742         return this.nodeHash[id];
20743     },
20744
20745     registerNode : function(node){
20746         this.nodeHash[node.id] = node;
20747     },
20748
20749     unregisterNode : function(node){
20750         delete this.nodeHash[node.id];
20751     },
20752
20753     toString : function(){
20754         return "[Tree"+(this.id?" "+this.id:"")+"]";
20755     }
20756 });
20757
20758 /**
20759  * @class Roo.data.Node
20760  * @extends Roo.util.Observable
20761  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20762  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20763  * @constructor
20764  * @param {Object} attributes The attributes/config for the node
20765  */
20766 Roo.data.Node = function(attributes){
20767     /**
20768      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20769      * @type {Object}
20770      */
20771     this.attributes = attributes || {};
20772     this.leaf = this.attributes.leaf;
20773     /**
20774      * The node id. @type String
20775      */
20776     this.id = this.attributes.id;
20777     if(!this.id){
20778         this.id = Roo.id(null, "ynode-");
20779         this.attributes.id = this.id;
20780     }
20781     /**
20782      * All child nodes of this node. @type Array
20783      */
20784     this.childNodes = [];
20785     if(!this.childNodes.indexOf){ // indexOf is a must
20786         this.childNodes.indexOf = function(o){
20787             for(var i = 0, len = this.length; i < len; i++){
20788                 if(this[i] == o) return i;
20789             }
20790             return -1;
20791         };
20792     }
20793     /**
20794      * The parent node for this node. @type Node
20795      */
20796     this.parentNode = null;
20797     /**
20798      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20799      */
20800     this.firstChild = null;
20801     /**
20802      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20803      */
20804     this.lastChild = null;
20805     /**
20806      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20807      */
20808     this.previousSibling = null;
20809     /**
20810      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20811      */
20812     this.nextSibling = null;
20813
20814     this.addEvents({
20815        /**
20816         * @event append
20817         * Fires when a new child node is appended
20818         * @param {Tree} tree The owner tree
20819         * @param {Node} this This node
20820         * @param {Node} node The newly appended node
20821         * @param {Number} index The index of the newly appended node
20822         */
20823        "append" : true,
20824        /**
20825         * @event remove
20826         * Fires when a child node is removed
20827         * @param {Tree} tree The owner tree
20828         * @param {Node} this This node
20829         * @param {Node} node The removed node
20830         */
20831        "remove" : true,
20832        /**
20833         * @event move
20834         * Fires when this node is moved to a new location in the tree
20835         * @param {Tree} tree The owner tree
20836         * @param {Node} this This node
20837         * @param {Node} oldParent The old parent of this node
20838         * @param {Node} newParent The new parent of this node
20839         * @param {Number} index The index it was moved to
20840         */
20841        "move" : true,
20842        /**
20843         * @event insert
20844         * Fires when a new child node is inserted.
20845         * @param {Tree} tree The owner tree
20846         * @param {Node} this This node
20847         * @param {Node} node The child node inserted
20848         * @param {Node} refNode The child node the node was inserted before
20849         */
20850        "insert" : true,
20851        /**
20852         * @event beforeappend
20853         * Fires before a new child is appended, return false to cancel the append.
20854         * @param {Tree} tree The owner tree
20855         * @param {Node} this This node
20856         * @param {Node} node The child node to be appended
20857         */
20858        "beforeappend" : true,
20859        /**
20860         * @event beforeremove
20861         * Fires before a child is removed, return false to cancel the remove.
20862         * @param {Tree} tree The owner tree
20863         * @param {Node} this This node
20864         * @param {Node} node The child node to be removed
20865         */
20866        "beforeremove" : true,
20867        /**
20868         * @event beforemove
20869         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20870         * @param {Tree} tree The owner tree
20871         * @param {Node} this This node
20872         * @param {Node} oldParent The parent of this node
20873         * @param {Node} newParent The new parent this node is moving to
20874         * @param {Number} index The index it is being moved to
20875         */
20876        "beforemove" : true,
20877        /**
20878         * @event beforeinsert
20879         * Fires before a new child is inserted, return false to cancel the insert.
20880         * @param {Tree} tree The owner tree
20881         * @param {Node} this This node
20882         * @param {Node} node The child node to be inserted
20883         * @param {Node} refNode The child node the node is being inserted before
20884         */
20885        "beforeinsert" : true
20886    });
20887     this.listeners = this.attributes.listeners;
20888     Roo.data.Node.superclass.constructor.call(this);
20889 };
20890
20891 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20892     fireEvent : function(evtName){
20893         // first do standard event for this node
20894         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20895             return false;
20896         }
20897         // then bubble it up to the tree if the event wasn't cancelled
20898         var ot = this.getOwnerTree();
20899         if(ot){
20900             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20901                 return false;
20902             }
20903         }
20904         return true;
20905     },
20906
20907     /**
20908      * Returns true if this node is a leaf
20909      * @return {Boolean}
20910      */
20911     isLeaf : function(){
20912         return this.leaf === true;
20913     },
20914
20915     // private
20916     setFirstChild : function(node){
20917         this.firstChild = node;
20918     },
20919
20920     //private
20921     setLastChild : function(node){
20922         this.lastChild = node;
20923     },
20924
20925
20926     /**
20927      * Returns true if this node is the last child of its parent
20928      * @return {Boolean}
20929      */
20930     isLast : function(){
20931        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20932     },
20933
20934     /**
20935      * Returns true if this node is the first child of its parent
20936      * @return {Boolean}
20937      */
20938     isFirst : function(){
20939        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20940     },
20941
20942     hasChildNodes : function(){
20943         return !this.isLeaf() && this.childNodes.length > 0;
20944     },
20945
20946     /**
20947      * Insert node(s) as the last child node of this node.
20948      * @param {Node/Array} node The node or Array of nodes to append
20949      * @return {Node} The appended node if single append, or null if an array was passed
20950      */
20951     appendChild : function(node){
20952         var multi = false;
20953         if(node instanceof Array){
20954             multi = node;
20955         }else if(arguments.length > 1){
20956             multi = arguments;
20957         }
20958         // if passed an array or multiple args do them one by one
20959         if(multi){
20960             for(var i = 0, len = multi.length; i < len; i++) {
20961                 this.appendChild(multi[i]);
20962             }
20963         }else{
20964             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
20965                 return false;
20966             }
20967             var index = this.childNodes.length;
20968             var oldParent = node.parentNode;
20969             // it's a move, make sure we move it cleanly
20970             if(oldParent){
20971                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
20972                     return false;
20973                 }
20974                 oldParent.removeChild(node);
20975             }
20976             index = this.childNodes.length;
20977             if(index == 0){
20978                 this.setFirstChild(node);
20979             }
20980             this.childNodes.push(node);
20981             node.parentNode = this;
20982             var ps = this.childNodes[index-1];
20983             if(ps){
20984                 node.previousSibling = ps;
20985                 ps.nextSibling = node;
20986             }else{
20987                 node.previousSibling = null;
20988             }
20989             node.nextSibling = null;
20990             this.setLastChild(node);
20991             node.setOwnerTree(this.getOwnerTree());
20992             this.fireEvent("append", this.ownerTree, this, node, index);
20993             if(oldParent){
20994                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
20995             }
20996             return node;
20997         }
20998     },
20999
21000     /**
21001      * Removes a child node from this node.
21002      * @param {Node} node The node to remove
21003      * @return {Node} The removed node
21004      */
21005     removeChild : function(node){
21006         var index = this.childNodes.indexOf(node);
21007         if(index == -1){
21008             return false;
21009         }
21010         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21011             return false;
21012         }
21013
21014         // remove it from childNodes collection
21015         this.childNodes.splice(index, 1);
21016
21017         // update siblings
21018         if(node.previousSibling){
21019             node.previousSibling.nextSibling = node.nextSibling;
21020         }
21021         if(node.nextSibling){
21022             node.nextSibling.previousSibling = node.previousSibling;
21023         }
21024
21025         // update child refs
21026         if(this.firstChild == node){
21027             this.setFirstChild(node.nextSibling);
21028         }
21029         if(this.lastChild == node){
21030             this.setLastChild(node.previousSibling);
21031         }
21032
21033         node.setOwnerTree(null);
21034         // clear any references from the node
21035         node.parentNode = null;
21036         node.previousSibling = null;
21037         node.nextSibling = null;
21038         this.fireEvent("remove", this.ownerTree, this, node);
21039         return node;
21040     },
21041
21042     /**
21043      * Inserts the first node before the second node in this nodes childNodes collection.
21044      * @param {Node} node The node to insert
21045      * @param {Node} refNode The node to insert before (if null the node is appended)
21046      * @return {Node} The inserted node
21047      */
21048     insertBefore : function(node, refNode){
21049         if(!refNode){ // like standard Dom, refNode can be null for append
21050             return this.appendChild(node);
21051         }
21052         // nothing to do
21053         if(node == refNode){
21054             return false;
21055         }
21056
21057         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21058             return false;
21059         }
21060         var index = this.childNodes.indexOf(refNode);
21061         var oldParent = node.parentNode;
21062         var refIndex = index;
21063
21064         // when moving internally, indexes will change after remove
21065         if(oldParent == this && this.childNodes.indexOf(node) < index){
21066             refIndex--;
21067         }
21068
21069         // it's a move, make sure we move it cleanly
21070         if(oldParent){
21071             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21072                 return false;
21073             }
21074             oldParent.removeChild(node);
21075         }
21076         if(refIndex == 0){
21077             this.setFirstChild(node);
21078         }
21079         this.childNodes.splice(refIndex, 0, node);
21080         node.parentNode = this;
21081         var ps = this.childNodes[refIndex-1];
21082         if(ps){
21083             node.previousSibling = ps;
21084             ps.nextSibling = node;
21085         }else{
21086             node.previousSibling = null;
21087         }
21088         node.nextSibling = refNode;
21089         refNode.previousSibling = node;
21090         node.setOwnerTree(this.getOwnerTree());
21091         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21092         if(oldParent){
21093             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21094         }
21095         return node;
21096     },
21097
21098     /**
21099      * Returns the child node at the specified index.
21100      * @param {Number} index
21101      * @return {Node}
21102      */
21103     item : function(index){
21104         return this.childNodes[index];
21105     },
21106
21107     /**
21108      * Replaces one child node in this node with another.
21109      * @param {Node} newChild The replacement node
21110      * @param {Node} oldChild The node to replace
21111      * @return {Node} The replaced node
21112      */
21113     replaceChild : function(newChild, oldChild){
21114         this.insertBefore(newChild, oldChild);
21115         this.removeChild(oldChild);
21116         return oldChild;
21117     },
21118
21119     /**
21120      * Returns the index of a child node
21121      * @param {Node} node
21122      * @return {Number} The index of the node or -1 if it was not found
21123      */
21124     indexOf : function(child){
21125         return this.childNodes.indexOf(child);
21126     },
21127
21128     /**
21129      * Returns the tree this node is in.
21130      * @return {Tree}
21131      */
21132     getOwnerTree : function(){
21133         // if it doesn't have one, look for one
21134         if(!this.ownerTree){
21135             var p = this;
21136             while(p){
21137                 if(p.ownerTree){
21138                     this.ownerTree = p.ownerTree;
21139                     break;
21140                 }
21141                 p = p.parentNode;
21142             }
21143         }
21144         return this.ownerTree;
21145     },
21146
21147     /**
21148      * Returns depth of this node (the root node has a depth of 0)
21149      * @return {Number}
21150      */
21151     getDepth : function(){
21152         var depth = 0;
21153         var p = this;
21154         while(p.parentNode){
21155             ++depth;
21156             p = p.parentNode;
21157         }
21158         return depth;
21159     },
21160
21161     // private
21162     setOwnerTree : function(tree){
21163         // if it's move, we need to update everyone
21164         if(tree != this.ownerTree){
21165             if(this.ownerTree){
21166                 this.ownerTree.unregisterNode(this);
21167             }
21168             this.ownerTree = tree;
21169             var cs = this.childNodes;
21170             for(var i = 0, len = cs.length; i < len; i++) {
21171                 cs[i].setOwnerTree(tree);
21172             }
21173             if(tree){
21174                 tree.registerNode(this);
21175             }
21176         }
21177     },
21178
21179     /**
21180      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21181      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21182      * @return {String} The path
21183      */
21184     getPath : function(attr){
21185         attr = attr || "id";
21186         var p = this.parentNode;
21187         var b = [this.attributes[attr]];
21188         while(p){
21189             b.unshift(p.attributes[attr]);
21190             p = p.parentNode;
21191         }
21192         var sep = this.getOwnerTree().pathSeparator;
21193         return sep + b.join(sep);
21194     },
21195
21196     /**
21197      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21198      * function call will be the scope provided or the current node. The arguments to the function
21199      * will be the args provided or the current node. If the function returns false at any point,
21200      * the bubble is stopped.
21201      * @param {Function} fn The function to call
21202      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21203      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21204      */
21205     bubble : function(fn, scope, args){
21206         var p = this;
21207         while(p){
21208             if(fn.call(scope || p, args || p) === false){
21209                 break;
21210             }
21211             p = p.parentNode;
21212         }
21213     },
21214
21215     /**
21216      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21217      * function call will be the scope provided or the current node. The arguments to the function
21218      * will be the args provided or the current node. If the function returns false at any point,
21219      * the cascade is stopped on that branch.
21220      * @param {Function} fn The function to call
21221      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21222      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21223      */
21224     cascade : function(fn, scope, args){
21225         if(fn.call(scope || this, args || this) !== false){
21226             var cs = this.childNodes;
21227             for(var i = 0, len = cs.length; i < len; i++) {
21228                 cs[i].cascade(fn, scope, args);
21229             }
21230         }
21231     },
21232
21233     /**
21234      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21235      * function call will be the scope provided or the current node. The arguments to the function
21236      * will be the args provided or the current node. If the function returns false at any point,
21237      * the iteration stops.
21238      * @param {Function} fn The function to call
21239      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21240      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21241      */
21242     eachChild : function(fn, scope, args){
21243         var cs = this.childNodes;
21244         for(var i = 0, len = cs.length; i < len; i++) {
21245                 if(fn.call(scope || this, args || cs[i]) === false){
21246                     break;
21247                 }
21248         }
21249     },
21250
21251     /**
21252      * Finds the first child that has the attribute with the specified value.
21253      * @param {String} attribute The attribute name
21254      * @param {Mixed} value The value to search for
21255      * @return {Node} The found child or null if none was found
21256      */
21257     findChild : function(attribute, value){
21258         var cs = this.childNodes;
21259         for(var i = 0, len = cs.length; i < len; i++) {
21260                 if(cs[i].attributes[attribute] == value){
21261                     return cs[i];
21262                 }
21263         }
21264         return null;
21265     },
21266
21267     /**
21268      * Finds the first child by a custom function. The child matches if the function passed
21269      * returns true.
21270      * @param {Function} fn
21271      * @param {Object} scope (optional)
21272      * @return {Node} The found child or null if none was found
21273      */
21274     findChildBy : function(fn, scope){
21275         var cs = this.childNodes;
21276         for(var i = 0, len = cs.length; i < len; i++) {
21277                 if(fn.call(scope||cs[i], cs[i]) === true){
21278                     return cs[i];
21279                 }
21280         }
21281         return null;
21282     },
21283
21284     /**
21285      * Sorts this nodes children using the supplied sort function
21286      * @param {Function} fn
21287      * @param {Object} scope (optional)
21288      */
21289     sort : function(fn, scope){
21290         var cs = this.childNodes;
21291         var len = cs.length;
21292         if(len > 0){
21293             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21294             cs.sort(sortFn);
21295             for(var i = 0; i < len; i++){
21296                 var n = cs[i];
21297                 n.previousSibling = cs[i-1];
21298                 n.nextSibling = cs[i+1];
21299                 if(i == 0){
21300                     this.setFirstChild(n);
21301                 }
21302                 if(i == len-1){
21303                     this.setLastChild(n);
21304                 }
21305             }
21306         }
21307     },
21308
21309     /**
21310      * Returns true if this node is an ancestor (at any point) of the passed node.
21311      * @param {Node} node
21312      * @return {Boolean}
21313      */
21314     contains : function(node){
21315         return node.isAncestor(this);
21316     },
21317
21318     /**
21319      * Returns true if the passed node is an ancestor (at any point) of this node.
21320      * @param {Node} node
21321      * @return {Boolean}
21322      */
21323     isAncestor : function(node){
21324         var p = this.parentNode;
21325         while(p){
21326             if(p == node){
21327                 return true;
21328             }
21329             p = p.parentNode;
21330         }
21331         return false;
21332     },
21333
21334     toString : function(){
21335         return "[Node"+(this.id?" "+this.id:"")+"]";
21336     }
21337 });/*
21338  * Based on:
21339  * Ext JS Library 1.1.1
21340  * Copyright(c) 2006-2007, Ext JS, LLC.
21341  *
21342  * Originally Released Under LGPL - original licence link has changed is not relivant.
21343  *
21344  * Fork - LGPL
21345  * <script type="text/javascript">
21346  */
21347  
21348
21349 /**
21350  * @class Roo.ComponentMgr
21351  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21352  * @singleton
21353  */
21354 Roo.ComponentMgr = function(){
21355     var all = new Roo.util.MixedCollection();
21356
21357     return {
21358         /**
21359          * Registers a component.
21360          * @param {Roo.Component} c The component
21361          */
21362         register : function(c){
21363             all.add(c);
21364         },
21365
21366         /**
21367          * Unregisters a component.
21368          * @param {Roo.Component} c The component
21369          */
21370         unregister : function(c){
21371             all.remove(c);
21372         },
21373
21374         /**
21375          * Returns a component by id
21376          * @param {String} id The component id
21377          */
21378         get : function(id){
21379             return all.get(id);
21380         },
21381
21382         /**
21383          * Registers a function that will be called when a specified component is added to ComponentMgr
21384          * @param {String} id The component id
21385          * @param {Funtction} fn The callback function
21386          * @param {Object} scope The scope of the callback
21387          */
21388         onAvailable : function(id, fn, scope){
21389             all.on("add", function(index, o){
21390                 if(o.id == id){
21391                     fn.call(scope || o, o);
21392                     all.un("add", fn, scope);
21393                 }
21394             });
21395         }
21396     };
21397 }();/*
21398  * Based on:
21399  * Ext JS Library 1.1.1
21400  * Copyright(c) 2006-2007, Ext JS, LLC.
21401  *
21402  * Originally Released Under LGPL - original licence link has changed is not relivant.
21403  *
21404  * Fork - LGPL
21405  * <script type="text/javascript">
21406  */
21407  
21408 /**
21409  * @class Roo.Component
21410  * @extends Roo.util.Observable
21411  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21412  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21413  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21414  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21415  * All visual components (widgets) that require rendering into a layout should subclass Component.
21416  * @constructor
21417  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21418  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21419  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21420  */
21421 Roo.Component = function(config){
21422     config = config || {};
21423     if(config.tagName || config.dom || typeof config == "string"){ // element object
21424         config = {el: config, id: config.id || config};
21425     }
21426     this.initialConfig = config;
21427
21428     Roo.apply(this, config);
21429     this.addEvents({
21430         /**
21431          * @event disable
21432          * Fires after the component is disabled.
21433              * @param {Roo.Component} this
21434              */
21435         disable : true,
21436         /**
21437          * @event enable
21438          * Fires after the component is enabled.
21439              * @param {Roo.Component} this
21440              */
21441         enable : true,
21442         /**
21443          * @event beforeshow
21444          * Fires before the component is shown.  Return false to stop the show.
21445              * @param {Roo.Component} this
21446              */
21447         beforeshow : true,
21448         /**
21449          * @event show
21450          * Fires after the component is shown.
21451              * @param {Roo.Component} this
21452              */
21453         show : true,
21454         /**
21455          * @event beforehide
21456          * Fires before the component is hidden. Return false to stop the hide.
21457              * @param {Roo.Component} this
21458              */
21459         beforehide : true,
21460         /**
21461          * @event hide
21462          * Fires after the component is hidden.
21463              * @param {Roo.Component} this
21464              */
21465         hide : true,
21466         /**
21467          * @event beforerender
21468          * Fires before the component is rendered. Return false to stop the render.
21469              * @param {Roo.Component} this
21470              */
21471         beforerender : true,
21472         /**
21473          * @event render
21474          * Fires after the component is rendered.
21475              * @param {Roo.Component} this
21476              */
21477         render : true,
21478         /**
21479          * @event beforedestroy
21480          * Fires before the component is destroyed. Return false to stop the destroy.
21481              * @param {Roo.Component} this
21482              */
21483         beforedestroy : true,
21484         /**
21485          * @event destroy
21486          * Fires after the component is destroyed.
21487              * @param {Roo.Component} this
21488              */
21489         destroy : true
21490     });
21491     if(!this.id){
21492         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21493     }
21494     Roo.ComponentMgr.register(this);
21495     Roo.Component.superclass.constructor.call(this);
21496     this.initComponent();
21497     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21498         this.render(this.renderTo);
21499         delete this.renderTo;
21500     }
21501 };
21502
21503 // private
21504 Roo.Component.AUTO_ID = 1000;
21505
21506 Roo.extend(Roo.Component, Roo.util.Observable, {
21507     /**
21508      * @property {Boolean} hidden
21509      * true if this component is hidden. Read-only.
21510      */
21511     hidden : false,
21512     /**
21513      * true if this component is disabled. Read-only.
21514      */
21515     disabled : false,
21516     /**
21517      * true if this component has been rendered. Read-only.
21518      */
21519     rendered : false,
21520     
21521     /** @cfg {String} disableClass
21522      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21523      */
21524     disabledClass : "x-item-disabled",
21525         /** @cfg {Boolean} allowDomMove
21526          * Whether the component can move the Dom node when rendering (defaults to true).
21527          */
21528     allowDomMove : true,
21529     /** @cfg {String} hideMode
21530      * How this component should hidden. Supported values are
21531      * "visibility" (css visibility), "offsets" (negative offset position) and
21532      * "display" (css display) - defaults to "display".
21533      */
21534     hideMode: 'display',
21535
21536     // private
21537     ctype : "Roo.Component",
21538
21539     /** @cfg {String} actionMode 
21540      * which property holds the element that used for  hide() / show() / disable() / enable()
21541      * default is 'el' 
21542      */
21543     actionMode : "el",
21544
21545     // private
21546     getActionEl : function(){
21547         return this[this.actionMode];
21548     },
21549
21550     initComponent : Roo.emptyFn,
21551     /**
21552      * If this is a lazy rendering component, render it to its container element.
21553      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21554      */
21555     render : function(container, position){
21556         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21557             if(!container && this.el){
21558                 this.el = Roo.get(this.el);
21559                 container = this.el.dom.parentNode;
21560                 this.allowDomMove = false;
21561             }
21562             this.container = Roo.get(container);
21563             this.rendered = true;
21564             if(position !== undefined){
21565                 if(typeof position == 'number'){
21566                     position = this.container.dom.childNodes[position];
21567                 }else{
21568                     position = Roo.getDom(position);
21569                 }
21570             }
21571             this.onRender(this.container, position || null);
21572             if(this.cls){
21573                 this.el.addClass(this.cls);
21574                 delete this.cls;
21575             }
21576             if(this.style){
21577                 this.el.applyStyles(this.style);
21578                 delete this.style;
21579             }
21580             this.fireEvent("render", this);
21581             this.afterRender(this.container);
21582             if(this.hidden){
21583                 this.hide();
21584             }
21585             if(this.disabled){
21586                 this.disable();
21587             }
21588         }
21589         return this;
21590     },
21591
21592     // private
21593     // default function is not really useful
21594     onRender : function(ct, position){
21595         if(this.el){
21596             this.el = Roo.get(this.el);
21597             if(this.allowDomMove !== false){
21598                 ct.dom.insertBefore(this.el.dom, position);
21599             }
21600         }
21601     },
21602
21603     // private
21604     getAutoCreate : function(){
21605         var cfg = typeof this.autoCreate == "object" ?
21606                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21607         if(this.id && !cfg.id){
21608             cfg.id = this.id;
21609         }
21610         return cfg;
21611     },
21612
21613     // private
21614     afterRender : Roo.emptyFn,
21615
21616     /**
21617      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21618      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21619      */
21620     destroy : function(){
21621         if(this.fireEvent("beforedestroy", this) !== false){
21622             this.purgeListeners();
21623             this.beforeDestroy();
21624             if(this.rendered){
21625                 this.el.removeAllListeners();
21626                 this.el.remove();
21627                 if(this.actionMode == "container"){
21628                     this.container.remove();
21629                 }
21630             }
21631             this.onDestroy();
21632             Roo.ComponentMgr.unregister(this);
21633             this.fireEvent("destroy", this);
21634         }
21635     },
21636
21637         // private
21638     beforeDestroy : function(){
21639
21640     },
21641
21642         // private
21643         onDestroy : function(){
21644
21645     },
21646
21647     /**
21648      * Returns the underlying {@link Roo.Element}.
21649      * @return {Roo.Element} The element
21650      */
21651     getEl : function(){
21652         return this.el;
21653     },
21654
21655     /**
21656      * Returns the id of this component.
21657      * @return {String}
21658      */
21659     getId : function(){
21660         return this.id;
21661     },
21662
21663     /**
21664      * Try to focus this component.
21665      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21666      * @return {Roo.Component} this
21667      */
21668     focus : function(selectText){
21669         if(this.rendered){
21670             this.el.focus();
21671             if(selectText === true){
21672                 this.el.dom.select();
21673             }
21674         }
21675         return this;
21676     },
21677
21678     // private
21679     blur : function(){
21680         if(this.rendered){
21681             this.el.blur();
21682         }
21683         return this;
21684     },
21685
21686     /**
21687      * Disable this component.
21688      * @return {Roo.Component} this
21689      */
21690     disable : function(){
21691         if(this.rendered){
21692             this.onDisable();
21693         }
21694         this.disabled = true;
21695         this.fireEvent("disable", this);
21696         return this;
21697     },
21698
21699         // private
21700     onDisable : function(){
21701         this.getActionEl().addClass(this.disabledClass);
21702         this.el.dom.disabled = true;
21703     },
21704
21705     /**
21706      * Enable this component.
21707      * @return {Roo.Component} this
21708      */
21709     enable : function(){
21710         if(this.rendered){
21711             this.onEnable();
21712         }
21713         this.disabled = false;
21714         this.fireEvent("enable", this);
21715         return this;
21716     },
21717
21718         // private
21719     onEnable : function(){
21720         this.getActionEl().removeClass(this.disabledClass);
21721         this.el.dom.disabled = false;
21722     },
21723
21724     /**
21725      * Convenience function for setting disabled/enabled by boolean.
21726      * @param {Boolean} disabled
21727      */
21728     setDisabled : function(disabled){
21729         this[disabled ? "disable" : "enable"]();
21730     },
21731
21732     /**
21733      * Show this component.
21734      * @return {Roo.Component} this
21735      */
21736     show: function(){
21737         if(this.fireEvent("beforeshow", this) !== false){
21738             this.hidden = false;
21739             if(this.rendered){
21740                 this.onShow();
21741             }
21742             this.fireEvent("show", this);
21743         }
21744         return this;
21745     },
21746
21747     // private
21748     onShow : function(){
21749         var ae = this.getActionEl();
21750         if(this.hideMode == 'visibility'){
21751             ae.dom.style.visibility = "visible";
21752         }else if(this.hideMode == 'offsets'){
21753             ae.removeClass('x-hidden');
21754         }else{
21755             ae.dom.style.display = "";
21756         }
21757     },
21758
21759     /**
21760      * Hide this component.
21761      * @return {Roo.Component} this
21762      */
21763     hide: function(){
21764         if(this.fireEvent("beforehide", this) !== false){
21765             this.hidden = true;
21766             if(this.rendered){
21767                 this.onHide();
21768             }
21769             this.fireEvent("hide", this);
21770         }
21771         return this;
21772     },
21773
21774     // private
21775     onHide : function(){
21776         var ae = this.getActionEl();
21777         if(this.hideMode == 'visibility'){
21778             ae.dom.style.visibility = "hidden";
21779         }else if(this.hideMode == 'offsets'){
21780             ae.addClass('x-hidden');
21781         }else{
21782             ae.dom.style.display = "none";
21783         }
21784     },
21785
21786     /**
21787      * Convenience function to hide or show this component by boolean.
21788      * @param {Boolean} visible True to show, false to hide
21789      * @return {Roo.Component} this
21790      */
21791     setVisible: function(visible){
21792         if(visible) {
21793             this.show();
21794         }else{
21795             this.hide();
21796         }
21797         return this;
21798     },
21799
21800     /**
21801      * Returns true if this component is visible.
21802      */
21803     isVisible : function(){
21804         return this.getActionEl().isVisible();
21805     },
21806
21807     cloneConfig : function(overrides){
21808         overrides = overrides || {};
21809         var id = overrides.id || Roo.id();
21810         var cfg = Roo.applyIf(overrides, this.initialConfig);
21811         cfg.id = id; // prevent dup id
21812         return new this.constructor(cfg);
21813     }
21814 });/*
21815  * Based on:
21816  * Ext JS Library 1.1.1
21817  * Copyright(c) 2006-2007, Ext JS, LLC.
21818  *
21819  * Originally Released Under LGPL - original licence link has changed is not relivant.
21820  *
21821  * Fork - LGPL
21822  * <script type="text/javascript">
21823  */
21824  (function(){ 
21825 /**
21826  * @class Roo.Layer
21827  * @extends Roo.Element
21828  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21829  * automatic maintaining of shadow/shim positions.
21830  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21831  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21832  * you can pass a string with a CSS class name. False turns off the shadow.
21833  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21834  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21835  * @cfg {String} cls CSS class to add to the element
21836  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21837  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21838  * @constructor
21839  * @param {Object} config An object with config options.
21840  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21841  */
21842
21843 Roo.Layer = function(config, existingEl){
21844     config = config || {};
21845     var dh = Roo.DomHelper;
21846     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21847     if(existingEl){
21848         this.dom = Roo.getDom(existingEl);
21849     }
21850     if(!this.dom){
21851         var o = config.dh || {tag: "div", cls: "x-layer"};
21852         this.dom = dh.append(pel, o);
21853     }
21854     if(config.cls){
21855         this.addClass(config.cls);
21856     }
21857     this.constrain = config.constrain !== false;
21858     this.visibilityMode = Roo.Element.VISIBILITY;
21859     if(config.id){
21860         this.id = this.dom.id = config.id;
21861     }else{
21862         this.id = Roo.id(this.dom);
21863     }
21864     this.zindex = config.zindex || this.getZIndex();
21865     this.position("absolute", this.zindex);
21866     if(config.shadow){
21867         this.shadowOffset = config.shadowOffset || 4;
21868         this.shadow = new Roo.Shadow({
21869             offset : this.shadowOffset,
21870             mode : config.shadow
21871         });
21872     }else{
21873         this.shadowOffset = 0;
21874     }
21875     this.useShim = config.shim !== false && Roo.useShims;
21876     this.useDisplay = config.useDisplay;
21877     this.hide();
21878 };
21879
21880 var supr = Roo.Element.prototype;
21881
21882 // shims are shared among layer to keep from having 100 iframes
21883 var shims = [];
21884
21885 Roo.extend(Roo.Layer, Roo.Element, {
21886
21887     getZIndex : function(){
21888         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21889     },
21890
21891     getShim : function(){
21892         if(!this.useShim){
21893             return null;
21894         }
21895         if(this.shim){
21896             return this.shim;
21897         }
21898         var shim = shims.shift();
21899         if(!shim){
21900             shim = this.createShim();
21901             shim.enableDisplayMode('block');
21902             shim.dom.style.display = 'none';
21903             shim.dom.style.visibility = 'visible';
21904         }
21905         var pn = this.dom.parentNode;
21906         if(shim.dom.parentNode != pn){
21907             pn.insertBefore(shim.dom, this.dom);
21908         }
21909         shim.setStyle('z-index', this.getZIndex()-2);
21910         this.shim = shim;
21911         return shim;
21912     },
21913
21914     hideShim : function(){
21915         if(this.shim){
21916             this.shim.setDisplayed(false);
21917             shims.push(this.shim);
21918             delete this.shim;
21919         }
21920     },
21921
21922     disableShadow : function(){
21923         if(this.shadow){
21924             this.shadowDisabled = true;
21925             this.shadow.hide();
21926             this.lastShadowOffset = this.shadowOffset;
21927             this.shadowOffset = 0;
21928         }
21929     },
21930
21931     enableShadow : function(show){
21932         if(this.shadow){
21933             this.shadowDisabled = false;
21934             this.shadowOffset = this.lastShadowOffset;
21935             delete this.lastShadowOffset;
21936             if(show){
21937                 this.sync(true);
21938             }
21939         }
21940     },
21941
21942     // private
21943     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21944     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21945     sync : function(doShow){
21946         var sw = this.shadow;
21947         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21948             var sh = this.getShim();
21949
21950             var w = this.getWidth(),
21951                 h = this.getHeight();
21952
21953             var l = this.getLeft(true),
21954                 t = this.getTop(true);
21955
21956             if(sw && !this.shadowDisabled){
21957                 if(doShow && !sw.isVisible()){
21958                     sw.show(this);
21959                 }else{
21960                     sw.realign(l, t, w, h);
21961                 }
21962                 if(sh){
21963                     if(doShow){
21964                        sh.show();
21965                     }
21966                     // fit the shim behind the shadow, so it is shimmed too
21967                     var a = sw.adjusts, s = sh.dom.style;
21968                     s.left = (Math.min(l, l+a.l))+"px";
21969                     s.top = (Math.min(t, t+a.t))+"px";
21970                     s.width = (w+a.w)+"px";
21971                     s.height = (h+a.h)+"px";
21972                 }
21973             }else if(sh){
21974                 if(doShow){
21975                    sh.show();
21976                 }
21977                 sh.setSize(w, h);
21978                 sh.setLeftTop(l, t);
21979             }
21980             
21981         }
21982     },
21983
21984     // private
21985     destroy : function(){
21986         this.hideShim();
21987         if(this.shadow){
21988             this.shadow.hide();
21989         }
21990         this.removeAllListeners();
21991         var pn = this.dom.parentNode;
21992         if(pn){
21993             pn.removeChild(this.dom);
21994         }
21995         Roo.Element.uncache(this.id);
21996     },
21997
21998     remove : function(){
21999         this.destroy();
22000     },
22001
22002     // private
22003     beginUpdate : function(){
22004         this.updating = true;
22005     },
22006
22007     // private
22008     endUpdate : function(){
22009         this.updating = false;
22010         this.sync(true);
22011     },
22012
22013     // private
22014     hideUnders : function(negOffset){
22015         if(this.shadow){
22016             this.shadow.hide();
22017         }
22018         this.hideShim();
22019     },
22020
22021     // private
22022     constrainXY : function(){
22023         if(this.constrain){
22024             var vw = Roo.lib.Dom.getViewWidth(),
22025                 vh = Roo.lib.Dom.getViewHeight();
22026             var s = Roo.get(document).getScroll();
22027
22028             var xy = this.getXY();
22029             var x = xy[0], y = xy[1];   
22030             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22031             // only move it if it needs it
22032             var moved = false;
22033             // first validate right/bottom
22034             if((x + w) > vw+s.left){
22035                 x = vw - w - this.shadowOffset;
22036                 moved = true;
22037             }
22038             if((y + h) > vh+s.top){
22039                 y = vh - h - this.shadowOffset;
22040                 moved = true;
22041             }
22042             // then make sure top/left isn't negative
22043             if(x < s.left){
22044                 x = s.left;
22045                 moved = true;
22046             }
22047             if(y < s.top){
22048                 y = s.top;
22049                 moved = true;
22050             }
22051             if(moved){
22052                 if(this.avoidY){
22053                     var ay = this.avoidY;
22054                     if(y <= ay && (y+h) >= ay){
22055                         y = ay-h-5;   
22056                     }
22057                 }
22058                 xy = [x, y];
22059                 this.storeXY(xy);
22060                 supr.setXY.call(this, xy);
22061                 this.sync();
22062             }
22063         }
22064     },
22065
22066     isVisible : function(){
22067         return this.visible;    
22068     },
22069
22070     // private
22071     showAction : function(){
22072         this.visible = true; // track visibility to prevent getStyle calls
22073         if(this.useDisplay === true){
22074             this.setDisplayed("");
22075         }else if(this.lastXY){
22076             supr.setXY.call(this, this.lastXY);
22077         }else if(this.lastLT){
22078             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22079         }
22080     },
22081
22082     // private
22083     hideAction : function(){
22084         this.visible = false;
22085         if(this.useDisplay === true){
22086             this.setDisplayed(false);
22087         }else{
22088             this.setLeftTop(-10000,-10000);
22089         }
22090     },
22091
22092     // overridden Element method
22093     setVisible : function(v, a, d, c, e){
22094         if(v){
22095             this.showAction();
22096         }
22097         if(a && v){
22098             var cb = function(){
22099                 this.sync(true);
22100                 if(c){
22101                     c();
22102                 }
22103             }.createDelegate(this);
22104             supr.setVisible.call(this, true, true, d, cb, e);
22105         }else{
22106             if(!v){
22107                 this.hideUnders(true);
22108             }
22109             var cb = c;
22110             if(a){
22111                 cb = function(){
22112                     this.hideAction();
22113                     if(c){
22114                         c();
22115                     }
22116                 }.createDelegate(this);
22117             }
22118             supr.setVisible.call(this, v, a, d, cb, e);
22119             if(v){
22120                 this.sync(true);
22121             }else if(!a){
22122                 this.hideAction();
22123             }
22124         }
22125     },
22126
22127     storeXY : function(xy){
22128         delete this.lastLT;
22129         this.lastXY = xy;
22130     },
22131
22132     storeLeftTop : function(left, top){
22133         delete this.lastXY;
22134         this.lastLT = [left, top];
22135     },
22136
22137     // private
22138     beforeFx : function(){
22139         this.beforeAction();
22140         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22141     },
22142
22143     // private
22144     afterFx : function(){
22145         Roo.Layer.superclass.afterFx.apply(this, arguments);
22146         this.sync(this.isVisible());
22147     },
22148
22149     // private
22150     beforeAction : function(){
22151         if(!this.updating && this.shadow){
22152             this.shadow.hide();
22153         }
22154     },
22155
22156     // overridden Element method
22157     setLeft : function(left){
22158         this.storeLeftTop(left, this.getTop(true));
22159         supr.setLeft.apply(this, arguments);
22160         this.sync();
22161     },
22162
22163     setTop : function(top){
22164         this.storeLeftTop(this.getLeft(true), top);
22165         supr.setTop.apply(this, arguments);
22166         this.sync();
22167     },
22168
22169     setLeftTop : function(left, top){
22170         this.storeLeftTop(left, top);
22171         supr.setLeftTop.apply(this, arguments);
22172         this.sync();
22173     },
22174
22175     setXY : function(xy, a, d, c, e){
22176         this.fixDisplay();
22177         this.beforeAction();
22178         this.storeXY(xy);
22179         var cb = this.createCB(c);
22180         supr.setXY.call(this, xy, a, d, cb, e);
22181         if(!a){
22182             cb();
22183         }
22184     },
22185
22186     // private
22187     createCB : function(c){
22188         var el = this;
22189         return function(){
22190             el.constrainXY();
22191             el.sync(true);
22192             if(c){
22193                 c();
22194             }
22195         };
22196     },
22197
22198     // overridden Element method
22199     setX : function(x, a, d, c, e){
22200         this.setXY([x, this.getY()], a, d, c, e);
22201     },
22202
22203     // overridden Element method
22204     setY : function(y, a, d, c, e){
22205         this.setXY([this.getX(), y], a, d, c, e);
22206     },
22207
22208     // overridden Element method
22209     setSize : function(w, h, a, d, c, e){
22210         this.beforeAction();
22211         var cb = this.createCB(c);
22212         supr.setSize.call(this, w, h, a, d, cb, e);
22213         if(!a){
22214             cb();
22215         }
22216     },
22217
22218     // overridden Element method
22219     setWidth : function(w, a, d, c, e){
22220         this.beforeAction();
22221         var cb = this.createCB(c);
22222         supr.setWidth.call(this, w, a, d, cb, e);
22223         if(!a){
22224             cb();
22225         }
22226     },
22227
22228     // overridden Element method
22229     setHeight : function(h, a, d, c, e){
22230         this.beforeAction();
22231         var cb = this.createCB(c);
22232         supr.setHeight.call(this, h, a, d, cb, e);
22233         if(!a){
22234             cb();
22235         }
22236     },
22237
22238     // overridden Element method
22239     setBounds : function(x, y, w, h, a, d, c, e){
22240         this.beforeAction();
22241         var cb = this.createCB(c);
22242         if(!a){
22243             this.storeXY([x, y]);
22244             supr.setXY.call(this, [x, y]);
22245             supr.setSize.call(this, w, h, a, d, cb, e);
22246             cb();
22247         }else{
22248             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22249         }
22250         return this;
22251     },
22252     
22253     /**
22254      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22255      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22256      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22257      * @param {Number} zindex The new z-index to set
22258      * @return {this} The Layer
22259      */
22260     setZIndex : function(zindex){
22261         this.zindex = zindex;
22262         this.setStyle("z-index", zindex + 2);
22263         if(this.shadow){
22264             this.shadow.setZIndex(zindex + 1);
22265         }
22266         if(this.shim){
22267             this.shim.setStyle("z-index", zindex);
22268         }
22269     }
22270 });
22271 })();/*
22272  * Based on:
22273  * Ext JS Library 1.1.1
22274  * Copyright(c) 2006-2007, Ext JS, LLC.
22275  *
22276  * Originally Released Under LGPL - original licence link has changed is not relivant.
22277  *
22278  * Fork - LGPL
22279  * <script type="text/javascript">
22280  */
22281
22282
22283 /**
22284  * @class Roo.Shadow
22285  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22286  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22287  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22288  * @constructor
22289  * Create a new Shadow
22290  * @param {Object} config The config object
22291  */
22292 Roo.Shadow = function(config){
22293     Roo.apply(this, config);
22294     if(typeof this.mode != "string"){
22295         this.mode = this.defaultMode;
22296     }
22297     var o = this.offset, a = {h: 0};
22298     var rad = Math.floor(this.offset/2);
22299     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22300         case "drop":
22301             a.w = 0;
22302             a.l = a.t = o;
22303             a.t -= 1;
22304             if(Roo.isIE){
22305                 a.l -= this.offset + rad;
22306                 a.t -= this.offset + rad;
22307                 a.w -= rad;
22308                 a.h -= rad;
22309                 a.t += 1;
22310             }
22311         break;
22312         case "sides":
22313             a.w = (o*2);
22314             a.l = -o;
22315             a.t = o-1;
22316             if(Roo.isIE){
22317                 a.l -= (this.offset - rad);
22318                 a.t -= this.offset + rad;
22319                 a.l += 1;
22320                 a.w -= (this.offset - rad)*2;
22321                 a.w -= rad + 1;
22322                 a.h -= 1;
22323             }
22324         break;
22325         case "frame":
22326             a.w = a.h = (o*2);
22327             a.l = a.t = -o;
22328             a.t += 1;
22329             a.h -= 2;
22330             if(Roo.isIE){
22331                 a.l -= (this.offset - rad);
22332                 a.t -= (this.offset - rad);
22333                 a.l += 1;
22334                 a.w -= (this.offset + rad + 1);
22335                 a.h -= (this.offset + rad);
22336                 a.h += 1;
22337             }
22338         break;
22339     };
22340
22341     this.adjusts = a;
22342 };
22343
22344 Roo.Shadow.prototype = {
22345     /**
22346      * @cfg {String} mode
22347      * The shadow display mode.  Supports the following options:<br />
22348      * sides: Shadow displays on both sides and bottom only<br />
22349      * frame: Shadow displays equally on all four sides<br />
22350      * drop: Traditional bottom-right drop shadow (default)
22351      */
22352     /**
22353      * @cfg {String} offset
22354      * The number of pixels to offset the shadow from the element (defaults to 4)
22355      */
22356     offset: 4,
22357
22358     // private
22359     defaultMode: "drop",
22360
22361     /**
22362      * Displays the shadow under the target element
22363      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22364      */
22365     show : function(target){
22366         target = Roo.get(target);
22367         if(!this.el){
22368             this.el = Roo.Shadow.Pool.pull();
22369             if(this.el.dom.nextSibling != target.dom){
22370                 this.el.insertBefore(target);
22371             }
22372         }
22373         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22374         if(Roo.isIE){
22375             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22376         }
22377         this.realign(
22378             target.getLeft(true),
22379             target.getTop(true),
22380             target.getWidth(),
22381             target.getHeight()
22382         );
22383         this.el.dom.style.display = "block";
22384     },
22385
22386     /**
22387      * Returns true if the shadow is visible, else false
22388      */
22389     isVisible : function(){
22390         return this.el ? true : false;  
22391     },
22392
22393     /**
22394      * Direct alignment when values are already available. Show must be called at least once before
22395      * calling this method to ensure it is initialized.
22396      * @param {Number} left The target element left position
22397      * @param {Number} top The target element top position
22398      * @param {Number} width The target element width
22399      * @param {Number} height The target element height
22400      */
22401     realign : function(l, t, w, h){
22402         if(!this.el){
22403             return;
22404         }
22405         var a = this.adjusts, d = this.el.dom, s = d.style;
22406         var iea = 0;
22407         s.left = (l+a.l)+"px";
22408         s.top = (t+a.t)+"px";
22409         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22410         if(s.width != sws || s.height != shs){
22411             s.width = sws;
22412             s.height = shs;
22413             if(!Roo.isIE){
22414                 var cn = d.childNodes;
22415                 var sww = Math.max(0, (sw-12))+"px";
22416                 cn[0].childNodes[1].style.width = sww;
22417                 cn[1].childNodes[1].style.width = sww;
22418                 cn[2].childNodes[1].style.width = sww;
22419                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22420             }
22421         }
22422     },
22423
22424     /**
22425      * Hides this shadow
22426      */
22427     hide : function(){
22428         if(this.el){
22429             this.el.dom.style.display = "none";
22430             Roo.Shadow.Pool.push(this.el);
22431             delete this.el;
22432         }
22433     },
22434
22435     /**
22436      * Adjust the z-index of this shadow
22437      * @param {Number} zindex The new z-index
22438      */
22439     setZIndex : function(z){
22440         this.zIndex = z;
22441         if(this.el){
22442             this.el.setStyle("z-index", z);
22443         }
22444     }
22445 };
22446
22447 // Private utility class that manages the internal Shadow cache
22448 Roo.Shadow.Pool = function(){
22449     var p = [];
22450     var markup = Roo.isIE ?
22451                  '<div class="x-ie-shadow"></div>' :
22452                  '<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>';
22453     return {
22454         pull : function(){
22455             var sh = p.shift();
22456             if(!sh){
22457                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22458                 sh.autoBoxAdjust = false;
22459             }
22460             return sh;
22461         },
22462
22463         push : function(sh){
22464             p.push(sh);
22465         }
22466     };
22467 }();/*
22468  * Based on:
22469  * Ext JS Library 1.1.1
22470  * Copyright(c) 2006-2007, Ext JS, LLC.
22471  *
22472  * Originally Released Under LGPL - original licence link has changed is not relivant.
22473  *
22474  * Fork - LGPL
22475  * <script type="text/javascript">
22476  */
22477
22478 /**
22479  * @class Roo.BoxComponent
22480  * @extends Roo.Component
22481  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22482  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22483  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22484  * layout containers.
22485  * @constructor
22486  * @param {Roo.Element/String/Object} config The configuration options.
22487  */
22488 Roo.BoxComponent = function(config){
22489     Roo.Component.call(this, config);
22490     this.addEvents({
22491         /**
22492          * @event resize
22493          * Fires after the component is resized.
22494              * @param {Roo.Component} this
22495              * @param {Number} adjWidth The box-adjusted width that was set
22496              * @param {Number} adjHeight The box-adjusted height that was set
22497              * @param {Number} rawWidth The width that was originally specified
22498              * @param {Number} rawHeight The height that was originally specified
22499              */
22500         resize : true,
22501         /**
22502          * @event move
22503          * Fires after the component is moved.
22504              * @param {Roo.Component} this
22505              * @param {Number} x The new x position
22506              * @param {Number} y The new y position
22507              */
22508         move : true
22509     });
22510 };
22511
22512 Roo.extend(Roo.BoxComponent, Roo.Component, {
22513     // private, set in afterRender to signify that the component has been rendered
22514     boxReady : false,
22515     // private, used to defer height settings to subclasses
22516     deferHeight: false,
22517     /** @cfg {Number} width
22518      * width (optional) size of component
22519      */
22520      /** @cfg {Number} height
22521      * height (optional) size of component
22522      */
22523      
22524     /**
22525      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22526      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22527      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22528      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22529      * @return {Roo.BoxComponent} this
22530      */
22531     setSize : function(w, h){
22532         // support for standard size objects
22533         if(typeof w == 'object'){
22534             h = w.height;
22535             w = w.width;
22536         }
22537         // not rendered
22538         if(!this.boxReady){
22539             this.width = w;
22540             this.height = h;
22541             return this;
22542         }
22543
22544         // prevent recalcs when not needed
22545         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22546             return this;
22547         }
22548         this.lastSize = {width: w, height: h};
22549
22550         var adj = this.adjustSize(w, h);
22551         var aw = adj.width, ah = adj.height;
22552         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22553             var rz = this.getResizeEl();
22554             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22555                 rz.setSize(aw, ah);
22556             }else if(!this.deferHeight && ah !== undefined){
22557                 rz.setHeight(ah);
22558             }else if(aw !== undefined){
22559                 rz.setWidth(aw);
22560             }
22561             this.onResize(aw, ah, w, h);
22562             this.fireEvent('resize', this, aw, ah, w, h);
22563         }
22564         return this;
22565     },
22566
22567     /**
22568      * Gets the current size of the component's underlying element.
22569      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22570      */
22571     getSize : function(){
22572         return this.el.getSize();
22573     },
22574
22575     /**
22576      * Gets the current XY position of the component's underlying element.
22577      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22578      * @return {Array} The XY position of the element (e.g., [100, 200])
22579      */
22580     getPosition : function(local){
22581         if(local === true){
22582             return [this.el.getLeft(true), this.el.getTop(true)];
22583         }
22584         return this.xy || this.el.getXY();
22585     },
22586
22587     /**
22588      * Gets the current box measurements of the component's underlying element.
22589      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22590      * @returns {Object} box An object in the format {x, y, width, height}
22591      */
22592     getBox : function(local){
22593         var s = this.el.getSize();
22594         if(local){
22595             s.x = this.el.getLeft(true);
22596             s.y = this.el.getTop(true);
22597         }else{
22598             var xy = this.xy || this.el.getXY();
22599             s.x = xy[0];
22600             s.y = xy[1];
22601         }
22602         return s;
22603     },
22604
22605     /**
22606      * Sets the current box measurements of the component's underlying element.
22607      * @param {Object} box An object in the format {x, y, width, height}
22608      * @returns {Roo.BoxComponent} this
22609      */
22610     updateBox : function(box){
22611         this.setSize(box.width, box.height);
22612         this.setPagePosition(box.x, box.y);
22613         return this;
22614     },
22615
22616     // protected
22617     getResizeEl : function(){
22618         return this.resizeEl || this.el;
22619     },
22620
22621     // protected
22622     getPositionEl : function(){
22623         return this.positionEl || this.el;
22624     },
22625
22626     /**
22627      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22628      * This method fires the move event.
22629      * @param {Number} left The new left
22630      * @param {Number} top The new top
22631      * @returns {Roo.BoxComponent} this
22632      */
22633     setPosition : function(x, y){
22634         this.x = x;
22635         this.y = y;
22636         if(!this.boxReady){
22637             return this;
22638         }
22639         var adj = this.adjustPosition(x, y);
22640         var ax = adj.x, ay = adj.y;
22641
22642         var el = this.getPositionEl();
22643         if(ax !== undefined || ay !== undefined){
22644             if(ax !== undefined && ay !== undefined){
22645                 el.setLeftTop(ax, ay);
22646             }else if(ax !== undefined){
22647                 el.setLeft(ax);
22648             }else if(ay !== undefined){
22649                 el.setTop(ay);
22650             }
22651             this.onPosition(ax, ay);
22652             this.fireEvent('move', this, ax, ay);
22653         }
22654         return this;
22655     },
22656
22657     /**
22658      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22659      * This method fires the move event.
22660      * @param {Number} x The new x position
22661      * @param {Number} y The new y position
22662      * @returns {Roo.BoxComponent} this
22663      */
22664     setPagePosition : function(x, y){
22665         this.pageX = x;
22666         this.pageY = y;
22667         if(!this.boxReady){
22668             return;
22669         }
22670         if(x === undefined || y === undefined){ // cannot translate undefined points
22671             return;
22672         }
22673         var p = this.el.translatePoints(x, y);
22674         this.setPosition(p.left, p.top);
22675         return this;
22676     },
22677
22678     // private
22679     onRender : function(ct, position){
22680         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22681         if(this.resizeEl){
22682             this.resizeEl = Roo.get(this.resizeEl);
22683         }
22684         if(this.positionEl){
22685             this.positionEl = Roo.get(this.positionEl);
22686         }
22687     },
22688
22689     // private
22690     afterRender : function(){
22691         Roo.BoxComponent.superclass.afterRender.call(this);
22692         this.boxReady = true;
22693         this.setSize(this.width, this.height);
22694         if(this.x || this.y){
22695             this.setPosition(this.x, this.y);
22696         }
22697         if(this.pageX || this.pageY){
22698             this.setPagePosition(this.pageX, this.pageY);
22699         }
22700     },
22701
22702     /**
22703      * Force the component's size to recalculate based on the underlying element's current height and width.
22704      * @returns {Roo.BoxComponent} this
22705      */
22706     syncSize : function(){
22707         delete this.lastSize;
22708         this.setSize(this.el.getWidth(), this.el.getHeight());
22709         return this;
22710     },
22711
22712     /**
22713      * Called after the component is resized, this method is empty by default but can be implemented by any
22714      * subclass that needs to perform custom logic after a resize occurs.
22715      * @param {Number} adjWidth The box-adjusted width that was set
22716      * @param {Number} adjHeight The box-adjusted height that was set
22717      * @param {Number} rawWidth The width that was originally specified
22718      * @param {Number} rawHeight The height that was originally specified
22719      */
22720     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22721
22722     },
22723
22724     /**
22725      * Called after the component is moved, this method is empty by default but can be implemented by any
22726      * subclass that needs to perform custom logic after a move occurs.
22727      * @param {Number} x The new x position
22728      * @param {Number} y The new y position
22729      */
22730     onPosition : function(x, y){
22731
22732     },
22733
22734     // private
22735     adjustSize : function(w, h){
22736         if(this.autoWidth){
22737             w = 'auto';
22738         }
22739         if(this.autoHeight){
22740             h = 'auto';
22741         }
22742         return {width : w, height: h};
22743     },
22744
22745     // private
22746     adjustPosition : function(x, y){
22747         return {x : x, y: y};
22748     }
22749 });/*
22750  * Based on:
22751  * Ext JS Library 1.1.1
22752  * Copyright(c) 2006-2007, Ext JS, LLC.
22753  *
22754  * Originally Released Under LGPL - original licence link has changed is not relivant.
22755  *
22756  * Fork - LGPL
22757  * <script type="text/javascript">
22758  */
22759
22760
22761 /**
22762  * @class Roo.SplitBar
22763  * @extends Roo.util.Observable
22764  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22765  * <br><br>
22766  * Usage:
22767  * <pre><code>
22768 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22769                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22770 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22771 split.minSize = 100;
22772 split.maxSize = 600;
22773 split.animate = true;
22774 split.on('moved', splitterMoved);
22775 </code></pre>
22776  * @constructor
22777  * Create a new SplitBar
22778  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22779  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22780  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22781  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22782                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22783                         position of the SplitBar).
22784  */
22785 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22786     
22787     /** @private */
22788     this.el = Roo.get(dragElement, true);
22789     this.el.dom.unselectable = "on";
22790     /** @private */
22791     this.resizingEl = Roo.get(resizingElement, true);
22792
22793     /**
22794      * @private
22795      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22796      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22797      * @type Number
22798      */
22799     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22800     
22801     /**
22802      * The minimum size of the resizing element. (Defaults to 0)
22803      * @type Number
22804      */
22805     this.minSize = 0;
22806     
22807     /**
22808      * The maximum size of the resizing element. (Defaults to 2000)
22809      * @type Number
22810      */
22811     this.maxSize = 2000;
22812     
22813     /**
22814      * Whether to animate the transition to the new size
22815      * @type Boolean
22816      */
22817     this.animate = false;
22818     
22819     /**
22820      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22821      * @type Boolean
22822      */
22823     this.useShim = false;
22824     
22825     /** @private */
22826     this.shim = null;
22827     
22828     if(!existingProxy){
22829         /** @private */
22830         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22831     }else{
22832         this.proxy = Roo.get(existingProxy).dom;
22833     }
22834     /** @private */
22835     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22836     
22837     /** @private */
22838     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22839     
22840     /** @private */
22841     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22842     
22843     /** @private */
22844     this.dragSpecs = {};
22845     
22846     /**
22847      * @private The adapter to use to positon and resize elements
22848      */
22849     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22850     this.adapter.init(this);
22851     
22852     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22853         /** @private */
22854         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22855         this.el.addClass("x-splitbar-h");
22856     }else{
22857         /** @private */
22858         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22859         this.el.addClass("x-splitbar-v");
22860     }
22861     
22862     this.addEvents({
22863         /**
22864          * @event resize
22865          * Fires when the splitter is moved (alias for {@link #event-moved})
22866          * @param {Roo.SplitBar} this
22867          * @param {Number} newSize the new width or height
22868          */
22869         "resize" : true,
22870         /**
22871          * @event moved
22872          * Fires when the splitter is moved
22873          * @param {Roo.SplitBar} this
22874          * @param {Number} newSize the new width or height
22875          */
22876         "moved" : true,
22877         /**
22878          * @event beforeresize
22879          * Fires before the splitter is dragged
22880          * @param {Roo.SplitBar} this
22881          */
22882         "beforeresize" : true,
22883
22884         "beforeapply" : true
22885     });
22886
22887     Roo.util.Observable.call(this);
22888 };
22889
22890 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22891     onStartProxyDrag : function(x, y){
22892         this.fireEvent("beforeresize", this);
22893         if(!this.overlay){
22894             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22895             o.unselectable();
22896             o.enableDisplayMode("block");
22897             // all splitbars share the same overlay
22898             Roo.SplitBar.prototype.overlay = o;
22899         }
22900         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22901         this.overlay.show();
22902         Roo.get(this.proxy).setDisplayed("block");
22903         var size = this.adapter.getElementSize(this);
22904         this.activeMinSize = this.getMinimumSize();;
22905         this.activeMaxSize = this.getMaximumSize();;
22906         var c1 = size - this.activeMinSize;
22907         var c2 = Math.max(this.activeMaxSize - size, 0);
22908         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22909             this.dd.resetConstraints();
22910             this.dd.setXConstraint(
22911                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22912                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22913             );
22914             this.dd.setYConstraint(0, 0);
22915         }else{
22916             this.dd.resetConstraints();
22917             this.dd.setXConstraint(0, 0);
22918             this.dd.setYConstraint(
22919                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22920                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22921             );
22922          }
22923         this.dragSpecs.startSize = size;
22924         this.dragSpecs.startPoint = [x, y];
22925         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22926     },
22927     
22928     /** 
22929      * @private Called after the drag operation by the DDProxy
22930      */
22931     onEndProxyDrag : function(e){
22932         Roo.get(this.proxy).setDisplayed(false);
22933         var endPoint = Roo.lib.Event.getXY(e);
22934         if(this.overlay){
22935             this.overlay.hide();
22936         }
22937         var newSize;
22938         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22939             newSize = this.dragSpecs.startSize + 
22940                 (this.placement == Roo.SplitBar.LEFT ?
22941                     endPoint[0] - this.dragSpecs.startPoint[0] :
22942                     this.dragSpecs.startPoint[0] - endPoint[0]
22943                 );
22944         }else{
22945             newSize = this.dragSpecs.startSize + 
22946                 (this.placement == Roo.SplitBar.TOP ?
22947                     endPoint[1] - this.dragSpecs.startPoint[1] :
22948                     this.dragSpecs.startPoint[1] - endPoint[1]
22949                 );
22950         }
22951         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22952         if(newSize != this.dragSpecs.startSize){
22953             if(this.fireEvent('beforeapply', this, newSize) !== false){
22954                 this.adapter.setElementSize(this, newSize);
22955                 this.fireEvent("moved", this, newSize);
22956                 this.fireEvent("resize", this, newSize);
22957             }
22958         }
22959     },
22960     
22961     /**
22962      * Get the adapter this SplitBar uses
22963      * @return The adapter object
22964      */
22965     getAdapter : function(){
22966         return this.adapter;
22967     },
22968     
22969     /**
22970      * Set the adapter this SplitBar uses
22971      * @param {Object} adapter A SplitBar adapter object
22972      */
22973     setAdapter : function(adapter){
22974         this.adapter = adapter;
22975         this.adapter.init(this);
22976     },
22977     
22978     /**
22979      * Gets the minimum size for the resizing element
22980      * @return {Number} The minimum size
22981      */
22982     getMinimumSize : function(){
22983         return this.minSize;
22984     },
22985     
22986     /**
22987      * Sets the minimum size for the resizing element
22988      * @param {Number} minSize The minimum size
22989      */
22990     setMinimumSize : function(minSize){
22991         this.minSize = minSize;
22992     },
22993     
22994     /**
22995      * Gets the maximum size for the resizing element
22996      * @return {Number} The maximum size
22997      */
22998     getMaximumSize : function(){
22999         return this.maxSize;
23000     },
23001     
23002     /**
23003      * Sets the maximum size for the resizing element
23004      * @param {Number} maxSize The maximum size
23005      */
23006     setMaximumSize : function(maxSize){
23007         this.maxSize = maxSize;
23008     },
23009     
23010     /**
23011      * Sets the initialize size for the resizing element
23012      * @param {Number} size The initial size
23013      */
23014     setCurrentSize : function(size){
23015         var oldAnimate = this.animate;
23016         this.animate = false;
23017         this.adapter.setElementSize(this, size);
23018         this.animate = oldAnimate;
23019     },
23020     
23021     /**
23022      * Destroy this splitbar. 
23023      * @param {Boolean} removeEl True to remove the element
23024      */
23025     destroy : function(removeEl){
23026         if(this.shim){
23027             this.shim.remove();
23028         }
23029         this.dd.unreg();
23030         this.proxy.parentNode.removeChild(this.proxy);
23031         if(removeEl){
23032             this.el.remove();
23033         }
23034     }
23035 });
23036
23037 /**
23038  * @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.
23039  */
23040 Roo.SplitBar.createProxy = function(dir){
23041     var proxy = new Roo.Element(document.createElement("div"));
23042     proxy.unselectable();
23043     var cls = 'x-splitbar-proxy';
23044     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23045     document.body.appendChild(proxy.dom);
23046     return proxy.dom;
23047 };
23048
23049 /** 
23050  * @class Roo.SplitBar.BasicLayoutAdapter
23051  * Default Adapter. It assumes the splitter and resizing element are not positioned
23052  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23053  */
23054 Roo.SplitBar.BasicLayoutAdapter = function(){
23055 };
23056
23057 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23058     // do nothing for now
23059     init : function(s){
23060     
23061     },
23062     /**
23063      * Called before drag operations to get the current size of the resizing element. 
23064      * @param {Roo.SplitBar} s The SplitBar using this adapter
23065      */
23066      getElementSize : function(s){
23067         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23068             return s.resizingEl.getWidth();
23069         }else{
23070             return s.resizingEl.getHeight();
23071         }
23072     },
23073     
23074     /**
23075      * Called after drag operations to set the size of the resizing element.
23076      * @param {Roo.SplitBar} s The SplitBar using this adapter
23077      * @param {Number} newSize The new size to set
23078      * @param {Function} onComplete A function to be invoked when resizing is complete
23079      */
23080     setElementSize : function(s, newSize, onComplete){
23081         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23082             if(!s.animate){
23083                 s.resizingEl.setWidth(newSize);
23084                 if(onComplete){
23085                     onComplete(s, newSize);
23086                 }
23087             }else{
23088                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23089             }
23090         }else{
23091             
23092             if(!s.animate){
23093                 s.resizingEl.setHeight(newSize);
23094                 if(onComplete){
23095                     onComplete(s, newSize);
23096                 }
23097             }else{
23098                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23099             }
23100         }
23101     }
23102 };
23103
23104 /** 
23105  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23106  * @extends Roo.SplitBar.BasicLayoutAdapter
23107  * Adapter that  moves the splitter element to align with the resized sizing element. 
23108  * Used with an absolute positioned SplitBar.
23109  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23110  * document.body, make sure you assign an id to the body element.
23111  */
23112 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23113     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23114     this.container = Roo.get(container);
23115 };
23116
23117 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23118     init : function(s){
23119         this.basic.init(s);
23120     },
23121     
23122     getElementSize : function(s){
23123         return this.basic.getElementSize(s);
23124     },
23125     
23126     setElementSize : function(s, newSize, onComplete){
23127         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23128     },
23129     
23130     moveSplitter : function(s){
23131         var yes = Roo.SplitBar;
23132         switch(s.placement){
23133             case yes.LEFT:
23134                 s.el.setX(s.resizingEl.getRight());
23135                 break;
23136             case yes.RIGHT:
23137                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23138                 break;
23139             case yes.TOP:
23140                 s.el.setY(s.resizingEl.getBottom());
23141                 break;
23142             case yes.BOTTOM:
23143                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23144                 break;
23145         }
23146     }
23147 };
23148
23149 /**
23150  * Orientation constant - Create a vertical SplitBar
23151  * @static
23152  * @type Number
23153  */
23154 Roo.SplitBar.VERTICAL = 1;
23155
23156 /**
23157  * Orientation constant - Create a horizontal SplitBar
23158  * @static
23159  * @type Number
23160  */
23161 Roo.SplitBar.HORIZONTAL = 2;
23162
23163 /**
23164  * Placement constant - The resizing element is to the left of the splitter element
23165  * @static
23166  * @type Number
23167  */
23168 Roo.SplitBar.LEFT = 1;
23169
23170 /**
23171  * Placement constant - The resizing element is to the right of the splitter element
23172  * @static
23173  * @type Number
23174  */
23175 Roo.SplitBar.RIGHT = 2;
23176
23177 /**
23178  * Placement constant - The resizing element is positioned above the splitter element
23179  * @static
23180  * @type Number
23181  */
23182 Roo.SplitBar.TOP = 3;
23183
23184 /**
23185  * Placement constant - The resizing element is positioned under splitter element
23186  * @static
23187  * @type Number
23188  */
23189 Roo.SplitBar.BOTTOM = 4;
23190 /*
23191  * Based on:
23192  * Ext JS Library 1.1.1
23193  * Copyright(c) 2006-2007, Ext JS, LLC.
23194  *
23195  * Originally Released Under LGPL - original licence link has changed is not relivant.
23196  *
23197  * Fork - LGPL
23198  * <script type="text/javascript">
23199  */
23200
23201 /**
23202  * @class Roo.View
23203  * @extends Roo.util.Observable
23204  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23205  * This class also supports single and multi selection modes. <br>
23206  * Create a data model bound view:
23207  <pre><code>
23208  var store = new Roo.data.Store(...);
23209
23210  var view = new Roo.View("my-element",
23211  '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23212  {
23213  singleSelect: true,
23214  selectedClass: "ydataview-selected",
23215  store: store
23216  });
23217
23218  // listen for node click?
23219  view.on("click", function(vw, index, node, e){
23220  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23221  });
23222
23223  // load XML data
23224  dataModel.load("foobar.xml");
23225  </code></pre>
23226  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23227  * <br><br>
23228  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23229  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23230  * @constructor
23231  * Create a new View
23232  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23233  * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
23234  * @param {Object} config The config object
23235  */
23236 Roo.View = function(container, tpl, config){
23237     this.el = Roo.get(container);
23238     if(typeof tpl == "string"){
23239         tpl = new Roo.Template(tpl);
23240     }
23241     tpl.compile();
23242     /**
23243      * The template used by this View
23244      * @type {Roo.DomHelper.Template}
23245      */
23246     this.tpl = tpl;
23247
23248     Roo.apply(this, config);
23249
23250     /** @private */
23251     this.addEvents({
23252     /**
23253      * @event beforeclick
23254      * Fires before a click is processed. Returns false to cancel the default action.
23255      * @param {Roo.View} this
23256      * @param {Number} index The index of the target node
23257      * @param {HTMLElement} node The target node
23258      * @param {Roo.EventObject} e The raw event object
23259      */
23260         "beforeclick" : true,
23261     /**
23262      * @event click
23263      * Fires when a template node is clicked.
23264      * @param {Roo.View} this
23265      * @param {Number} index The index of the target node
23266      * @param {HTMLElement} node The target node
23267      * @param {Roo.EventObject} e The raw event object
23268      */
23269         "click" : true,
23270     /**
23271      * @event dblclick
23272      * Fires when a template node is double clicked.
23273      * @param {Roo.View} this
23274      * @param {Number} index The index of the target node
23275      * @param {HTMLElement} node The target node
23276      * @param {Roo.EventObject} e The raw event object
23277      */
23278         "dblclick" : true,
23279     /**
23280      * @event contextmenu
23281      * Fires when a template node is right clicked.
23282      * @param {Roo.View} this
23283      * @param {Number} index The index of the target node
23284      * @param {HTMLElement} node The target node
23285      * @param {Roo.EventObject} e The raw event object
23286      */
23287         "contextmenu" : true,
23288     /**
23289      * @event selectionchange
23290      * Fires when the selected nodes change.
23291      * @param {Roo.View} this
23292      * @param {Array} selections Array of the selected nodes
23293      */
23294         "selectionchange" : true,
23295
23296     /**
23297      * @event beforeselect
23298      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23299      * @param {Roo.View} this
23300      * @param {HTMLElement} node The node to be selected
23301      * @param {Array} selections Array of currently selected nodes
23302      */
23303         "beforeselect" : true
23304     });
23305
23306     this.el.on({
23307         "click": this.onClick,
23308         "dblclick": this.onDblClick,
23309         "contextmenu": this.onContextMenu,
23310         scope:this
23311     });
23312
23313     this.selections = [];
23314     this.nodes = [];
23315     this.cmp = new Roo.CompositeElementLite([]);
23316     if(this.store){
23317         this.store = Roo.factory(this.store, Roo.data);
23318         this.setStore(this.store, true);
23319     }
23320     Roo.View.superclass.constructor.call(this);
23321 };
23322
23323 Roo.extend(Roo.View, Roo.util.Observable, {
23324     /**
23325      * The css class to add to selected nodes
23326      * @type {Roo.DomHelper.Template}
23327      */
23328     selectedClass : "x-view-selected",
23329     
23330     emptyText : "",
23331     /**
23332      * Returns the element this view is bound to.
23333      * @return {Roo.Element}
23334      */
23335     getEl : function(){
23336         return this.el;
23337     },
23338
23339     /**
23340      * Refreshes the view.
23341      */
23342     refresh : function(){
23343         var t = this.tpl;
23344         this.clearSelections();
23345         this.el.update("");
23346         var html = [];
23347         var records = this.store.getRange();
23348         if(records.length < 1){
23349             this.el.update(this.emptyText);
23350             return;
23351         }
23352         for(var i = 0, len = records.length; i < len; i++){
23353             var data = this.prepareData(records[i].data, i, records[i]);
23354             html[html.length] = t.apply(data);
23355         }
23356         this.el.update(html.join(""));
23357         this.nodes = this.el.dom.childNodes;
23358         this.updateIndexes(0);
23359     },
23360
23361     /**
23362      * Function to override to reformat the data that is sent to
23363      * the template for each node.
23364      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23365      * a JSON object for an UpdateManager bound view).
23366      */
23367     prepareData : function(data){
23368         return data;
23369     },
23370
23371     onUpdate : function(ds, record){
23372         this.clearSelections();
23373         var index = this.store.indexOf(record);
23374         var n = this.nodes[index];
23375         this.tpl.insertBefore(n, this.prepareData(record.data));
23376         n.parentNode.removeChild(n);
23377         this.updateIndexes(index, index);
23378     },
23379
23380     onAdd : function(ds, records, index){
23381         this.clearSelections();
23382         if(this.nodes.length == 0){
23383             this.refresh();
23384             return;
23385         }
23386         var n = this.nodes[index];
23387         for(var i = 0, len = records.length; i < len; i++){
23388             var d = this.prepareData(records[i].data);
23389             if(n){
23390                 this.tpl.insertBefore(n, d);
23391             }else{
23392                 this.tpl.append(this.el, d);
23393             }
23394         }
23395         this.updateIndexes(index);
23396     },
23397
23398     onRemove : function(ds, record, index){
23399         this.clearSelections();
23400         this.el.dom.removeChild(this.nodes[index]);
23401         this.updateIndexes(index);
23402     },
23403
23404     /**
23405      * Refresh an individual node.
23406      * @param {Number} index
23407      */
23408     refreshNode : function(index){
23409         this.onUpdate(this.store, this.store.getAt(index));
23410     },
23411
23412     updateIndexes : function(startIndex, endIndex){
23413         var ns = this.nodes;
23414         startIndex = startIndex || 0;
23415         endIndex = endIndex || ns.length - 1;
23416         for(var i = startIndex; i <= endIndex; i++){
23417             ns[i].nodeIndex = i;
23418         }
23419     },
23420
23421     /**
23422      * Changes the data store this view uses and refresh the view.
23423      * @param {Store} store
23424      */
23425     setStore : function(store, initial){
23426         if(!initial && this.store){
23427             this.store.un("datachanged", this.refresh);
23428             this.store.un("add", this.onAdd);
23429             this.store.un("remove", this.onRemove);
23430             this.store.un("update", this.onUpdate);
23431             this.store.un("clear", this.refresh);
23432         }
23433         if(store){
23434           
23435             store.on("datachanged", this.refresh, this);
23436             store.on("add", this.onAdd, this);
23437             store.on("remove", this.onRemove, this);
23438             store.on("update", this.onUpdate, this);
23439             store.on("clear", this.refresh, this);
23440         }
23441         
23442         if(store){
23443             this.refresh();
23444         }
23445     },
23446
23447     /**
23448      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23449      * @param {HTMLElement} node
23450      * @return {HTMLElement} The template node
23451      */
23452     findItemFromChild : function(node){
23453         var el = this.el.dom;
23454         if(!node || node.parentNode == el){
23455                     return node;
23456             }
23457             var p = node.parentNode;
23458             while(p && p != el){
23459             if(p.parentNode == el){
23460                 return p;
23461             }
23462             p = p.parentNode;
23463         }
23464             return null;
23465     },
23466
23467     /** @ignore */
23468     onClick : function(e){
23469         var item = this.findItemFromChild(e.getTarget());
23470         if(item){
23471             var index = this.indexOf(item);
23472             if(this.onItemClick(item, index, e) !== false){
23473                 this.fireEvent("click", this, index, item, e);
23474             }
23475         }else{
23476             this.clearSelections();
23477         }
23478     },
23479
23480     /** @ignore */
23481     onContextMenu : function(e){
23482         var item = this.findItemFromChild(e.getTarget());
23483         if(item){
23484             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23485         }
23486     },
23487
23488     /** @ignore */
23489     onDblClick : function(e){
23490         var item = this.findItemFromChild(e.getTarget());
23491         if(item){
23492             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23493         }
23494     },
23495
23496     onItemClick : function(item, index, e){
23497         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23498             return false;
23499         }
23500         if(this.multiSelect || this.singleSelect){
23501             if(this.multiSelect && e.shiftKey && this.lastSelection){
23502                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23503             }else{
23504                 this.select(item, this.multiSelect && e.ctrlKey);
23505                 this.lastSelection = item;
23506             }
23507             e.preventDefault();
23508         }
23509         return true;
23510     },
23511
23512     /**
23513      * Get the number of selected nodes.
23514      * @return {Number}
23515      */
23516     getSelectionCount : function(){
23517         return this.selections.length;
23518     },
23519
23520     /**
23521      * Get the currently selected nodes.
23522      * @return {Array} An array of HTMLElements
23523      */
23524     getSelectedNodes : function(){
23525         return this.selections;
23526     },
23527
23528     /**
23529      * Get the indexes of the selected nodes.
23530      * @return {Array}
23531      */
23532     getSelectedIndexes : function(){
23533         var indexes = [], s = this.selections;
23534         for(var i = 0, len = s.length; i < len; i++){
23535             indexes.push(s[i].nodeIndex);
23536         }
23537         return indexes;
23538     },
23539
23540     /**
23541      * Clear all selections
23542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23543      */
23544     clearSelections : function(suppressEvent){
23545         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23546             this.cmp.elements = this.selections;
23547             this.cmp.removeClass(this.selectedClass);
23548             this.selections = [];
23549             if(!suppressEvent){
23550                 this.fireEvent("selectionchange", this, this.selections);
23551             }
23552         }
23553     },
23554
23555     /**
23556      * Returns true if the passed node is selected
23557      * @param {HTMLElement/Number} node The node or node index
23558      * @return {Boolean}
23559      */
23560     isSelected : function(node){
23561         var s = this.selections;
23562         if(s.length < 1){
23563             return false;
23564         }
23565         node = this.getNode(node);
23566         return s.indexOf(node) !== -1;
23567     },
23568
23569     /**
23570      * Selects nodes.
23571      * @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
23572      * @param {Boolean} keepExisting (optional) true to keep existing selections
23573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23574      */
23575     select : function(nodeInfo, keepExisting, suppressEvent){
23576         if(nodeInfo instanceof Array){
23577             if(!keepExisting){
23578                 this.clearSelections(true);
23579             }
23580             for(var i = 0, len = nodeInfo.length; i < len; i++){
23581                 this.select(nodeInfo[i], true, true);
23582             }
23583         } else{
23584             var node = this.getNode(nodeInfo);
23585             if(node && !this.isSelected(node)){
23586                 if(!keepExisting){
23587                     this.clearSelections(true);
23588                 }
23589                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23590                     Roo.fly(node).addClass(this.selectedClass);
23591                     this.selections.push(node);
23592                     if(!suppressEvent){
23593                         this.fireEvent("selectionchange", this, this.selections);
23594                     }
23595                 }
23596             }
23597         }
23598     },
23599
23600     /**
23601      * Gets a template node.
23602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23603      * @return {HTMLElement} The node or null if it wasn't found
23604      */
23605     getNode : function(nodeInfo){
23606         if(typeof nodeInfo == "string"){
23607             return document.getElementById(nodeInfo);
23608         }else if(typeof nodeInfo == "number"){
23609             return this.nodes[nodeInfo];
23610         }
23611         return nodeInfo;
23612     },
23613
23614     /**
23615      * Gets a range template nodes.
23616      * @param {Number} startIndex
23617      * @param {Number} endIndex
23618      * @return {Array} An array of nodes
23619      */
23620     getNodes : function(start, end){
23621         var ns = this.nodes;
23622         start = start || 0;
23623         end = typeof end == "undefined" ? ns.length - 1 : end;
23624         var nodes = [];
23625         if(start <= end){
23626             for(var i = start; i <= end; i++){
23627                 nodes.push(ns[i]);
23628             }
23629         } else{
23630             for(var i = start; i >= end; i--){
23631                 nodes.push(ns[i]);
23632             }
23633         }
23634         return nodes;
23635     },
23636
23637     /**
23638      * Finds the index of the passed node
23639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23640      * @return {Number} The index of the node or -1
23641      */
23642     indexOf : function(node){
23643         node = this.getNode(node);
23644         if(typeof node.nodeIndex == "number"){
23645             return node.nodeIndex;
23646         }
23647         var ns = this.nodes;
23648         for(var i = 0, len = ns.length; i < len; i++){
23649             if(ns[i] == node){
23650                 return i;
23651             }
23652         }
23653         return -1;
23654     }
23655 });
23656 /*
23657  * Based on:
23658  * Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  *
23661  * Originally Released Under LGPL - original licence link has changed is not relivant.
23662  *
23663  * Fork - LGPL
23664  * <script type="text/javascript">
23665  */
23666
23667 /**
23668  * @class Roo.JsonView
23669  * @extends Roo.View
23670  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23671 <pre><code>
23672 var view = new Roo.JsonView("my-element",
23673     '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23674     { multiSelect: true, jsonRoot: "data" }
23675 );
23676
23677 // listen for node click?
23678 view.on("click", function(vw, index, node, e){
23679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23680 });
23681
23682 // direct load of JSON data
23683 view.load("foobar.php");
23684
23685 // Example from my blog list
23686 var tpl = new Roo.Template(
23687     '&lt;div class="entry"&gt;' +
23688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23690     "&lt;/div&gt;&lt;hr /&gt;"
23691 );
23692
23693 var moreView = new Roo.JsonView("entry-list", tpl, {
23694     jsonRoot: "posts"
23695 });
23696 moreView.on("beforerender", this.sortEntries, this);
23697 moreView.load({
23698     url: "/blog/get-posts.php",
23699     params: "allposts=true",
23700     text: "Loading Blog Entries..."
23701 });
23702 </code></pre>
23703  * @constructor
23704  * Create a new JsonView
23705  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23706  * @param {Template} tpl The rendering template
23707  * @param {Object} config The config object
23708  */
23709 Roo.JsonView = function(container, tpl, config){
23710     Roo.JsonView.superclass.constructor.call(this, container, tpl, config);
23711
23712     var um = this.el.getUpdateManager();
23713     um.setRenderer(this);
23714     um.on("update", this.onLoad, this);
23715     um.on("failure", this.onLoadException, this);
23716
23717     /**
23718      * @event beforerender
23719      * Fires before rendering of the downloaded JSON data.
23720      * @param {Roo.JsonView} this
23721      * @param {Object} data The JSON data loaded
23722      */
23723     /**
23724      * @event load
23725      * Fires when data is loaded.
23726      * @param {Roo.JsonView} this
23727      * @param {Object} data The JSON data loaded
23728      * @param {Object} response The raw Connect response object
23729      */
23730     /**
23731      * @event loadexception
23732      * Fires when loading fails.
23733      * @param {Roo.JsonView} this
23734      * @param {Object} response The raw Connect response object
23735      */
23736     this.addEvents({
23737         'beforerender' : true,
23738         'load' : true,
23739         'loadexception' : true
23740     });
23741 };
23742 Roo.extend(Roo.JsonView, Roo.View, {
23743     /**
23744      * The root property in the loaded JSON object that contains the data
23745      * @type {String}
23746      */
23747     jsonRoot : "",
23748
23749     /**
23750      * Refreshes the view.
23751      */
23752     refresh : function(){
23753         this.clearSelections();
23754         this.el.update("");
23755         var html = [];
23756         var o = this.jsonData;
23757         if(o && o.length > 0){
23758             for(var i = 0, len = o.length; i < len; i++){
23759                 var data = this.prepareData(o[i], i, o);
23760                 html[html.length] = this.tpl.apply(data);
23761             }
23762         }else{
23763             html.push(this.emptyText);
23764         }
23765         this.el.update(html.join(""));
23766         this.nodes = this.el.dom.childNodes;
23767         this.updateIndexes(0);
23768     },
23769
23770     /**
23771      * 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.
23772      * @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:
23773      <pre><code>
23774      view.load({
23775          url: "your-url.php",
23776          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23777          callback: yourFunction,
23778          scope: yourObject, //(optional scope)
23779          discardUrl: false,
23780          nocache: false,
23781          text: "Loading...",
23782          timeout: 30,
23783          scripts: false
23784      });
23785      </code></pre>
23786      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23787      * 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.
23788      * @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}
23789      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23790      * @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.
23791      */
23792     load : function(){
23793         var um = this.el.getUpdateManager();
23794         um.update.apply(um, arguments);
23795     },
23796
23797     render : function(el, response){
23798         this.clearSelections();
23799         this.el.update("");
23800         var o;
23801         try{
23802             o = Roo.util.JSON.decode(response.responseText);
23803             if(this.jsonRoot){
23804                 
23805                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23806             }
23807         } catch(e){
23808         }
23809         /**
23810          * The current JSON data or null
23811          */
23812         this.jsonData = o;
23813         this.beforeRender();
23814         this.refresh();
23815     },
23816
23817 /**
23818  * Get the number of records in the current JSON dataset
23819  * @return {Number}
23820  */
23821     getCount : function(){
23822         return this.jsonData ? this.jsonData.length : 0;
23823     },
23824
23825 /**
23826  * Returns the JSON object for the specified node(s)
23827  * @param {HTMLElement/Array} node The node or an array of nodes
23828  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23829  * you get the JSON object for the node
23830  */
23831     getNodeData : function(node){
23832         if(node instanceof Array){
23833             var data = [];
23834             for(var i = 0, len = node.length; i < len; i++){
23835                 data.push(this.getNodeData(node[i]));
23836             }
23837             return data;
23838         }
23839         return this.jsonData[this.indexOf(node)] || null;
23840     },
23841
23842     beforeRender : function(){
23843         this.snapshot = this.jsonData;
23844         if(this.sortInfo){
23845             this.sort.apply(this, this.sortInfo);
23846         }
23847         this.fireEvent("beforerender", this, this.jsonData);
23848     },
23849
23850     onLoad : function(el, o){
23851         this.fireEvent("load", this, this.jsonData, o);
23852     },
23853
23854     onLoadException : function(el, o){
23855         this.fireEvent("loadexception", this, o);
23856     },
23857
23858 /**
23859  * Filter the data by a specific property.
23860  * @param {String} property A property on your JSON objects
23861  * @param {String/RegExp} value Either string that the property values
23862  * should start with, or a RegExp to test against the property
23863  */
23864     filter : function(property, value){
23865         if(this.jsonData){
23866             var data = [];
23867             var ss = this.snapshot;
23868             if(typeof value == "string"){
23869                 var vlen = value.length;
23870                 if(vlen == 0){
23871                     this.clearFilter();
23872                     return;
23873                 }
23874                 value = value.toLowerCase();
23875                 for(var i = 0, len = ss.length; i < len; i++){
23876                     var o = ss[i];
23877                     if(o[property].substr(0, vlen).toLowerCase() == value){
23878                         data.push(o);
23879                     }
23880                 }
23881             } else if(value.exec){ // regex?
23882                 for(var i = 0, len = ss.length; i < len; i++){
23883                     var o = ss[i];
23884                     if(value.test(o[property])){
23885                         data.push(o);
23886                     }
23887                 }
23888             } else{
23889                 return;
23890             }
23891             this.jsonData = data;
23892             this.refresh();
23893         }
23894     },
23895
23896 /**
23897  * Filter by a function. The passed function will be called with each
23898  * object in the current dataset. If the function returns true the value is kept,
23899  * otherwise it is filtered.
23900  * @param {Function} fn
23901  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23902  */
23903     filterBy : function(fn, scope){
23904         if(this.jsonData){
23905             var data = [];
23906             var ss = this.snapshot;
23907             for(var i = 0, len = ss.length; i < len; i++){
23908                 var o = ss[i];
23909                 if(fn.call(scope || this, o)){
23910                     data.push(o);
23911                 }
23912             }
23913             this.jsonData = data;
23914             this.refresh();
23915         }
23916     },
23917
23918 /**
23919  * Clears the current filter.
23920  */
23921     clearFilter : function(){
23922         if(this.snapshot && this.jsonData != this.snapshot){
23923             this.jsonData = this.snapshot;
23924             this.refresh();
23925         }
23926     },
23927
23928
23929 /**
23930  * Sorts the data for this view and refreshes it.
23931  * @param {String} property A property on your JSON objects to sort on
23932  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
23933  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
23934  */
23935     sort : function(property, dir, sortType){
23936         this.sortInfo = Array.prototype.slice.call(arguments, 0);
23937         if(this.jsonData){
23938             var p = property;
23939             var dsc = dir && dir.toLowerCase() == "desc";
23940             var f = function(o1, o2){
23941                 var v1 = sortType ? sortType(o1[p]) : o1[p];
23942                 var v2 = sortType ? sortType(o2[p]) : o2[p];
23943                 ;
23944                 if(v1 < v2){
23945                     return dsc ? +1 : -1;
23946                 } else if(v1 > v2){
23947                     return dsc ? -1 : +1;
23948                 } else{
23949                     return 0;
23950                 }
23951             };
23952             this.jsonData.sort(f);
23953             this.refresh();
23954             if(this.jsonData != this.snapshot){
23955                 this.snapshot.sort(f);
23956             }
23957         }
23958     }
23959 });/*
23960  * Based on:
23961  * Ext JS Library 1.1.1
23962  * Copyright(c) 2006-2007, Ext JS, LLC.
23963  *
23964  * Originally Released Under LGPL - original licence link has changed is not relivant.
23965  *
23966  * Fork - LGPL
23967  * <script type="text/javascript">
23968  */
23969  
23970
23971 /**
23972  * @class Roo.ColorPalette
23973  * @extends Roo.Component
23974  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
23975  * Here's an example of typical usage:
23976  * <pre><code>
23977 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
23978 cp.render('my-div');
23979
23980 cp.on('select', function(palette, selColor){
23981     // do something with selColor
23982 });
23983 </code></pre>
23984  * @constructor
23985  * Create a new ColorPalette
23986  * @param {Object} config The config object
23987  */
23988 Roo.ColorPalette = function(config){
23989     Roo.ColorPalette.superclass.constructor.call(this, config);
23990     this.addEvents({
23991         /**
23992              * @event select
23993              * Fires when a color is selected
23994              * @param {ColorPalette} this
23995              * @param {String} color The 6-digit color hex code (without the # symbol)
23996              */
23997         select: true
23998     });
23999
24000     if(this.handler){
24001         this.on("select", this.handler, this.scope, true);
24002     }
24003 };
24004 Roo.extend(Roo.ColorPalette, Roo.Component, {
24005     /**
24006      * @cfg {String} itemCls
24007      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24008      */
24009     itemCls : "x-color-palette",
24010     /**
24011      * @cfg {String} value
24012      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24013      * the hex codes are case-sensitive.
24014      */
24015     value : null,
24016     clickEvent:'click',
24017     // private
24018     ctype: "Roo.ColorPalette",
24019
24020     /**
24021      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24022      */
24023     allowReselect : false,
24024
24025     /**
24026      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24027      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24028      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24029      * of colors with the width setting until the box is symmetrical.</p>
24030      * <p>You can override individual colors if needed:</p>
24031      * <pre><code>
24032 var cp = new Roo.ColorPalette();
24033 cp.colors[0] = "FF0000";  // change the first box to red
24034 </code></pre>
24035
24036 Or you can provide a custom array of your own for complete control:
24037 <pre><code>
24038 var cp = new Roo.ColorPalette();
24039 cp.colors = ["000000", "993300", "333300"];
24040 </code></pre>
24041      * @type Array
24042      */
24043     colors : [
24044         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24045         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24046         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24047         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24048         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24049     ],
24050
24051     // private
24052     onRender : function(container, position){
24053         var t = new Roo.MasterTemplate(
24054             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24055         );
24056         var c = this.colors;
24057         for(var i = 0, len = c.length; i < len; i++){
24058             t.add([c[i]]);
24059         }
24060         var el = document.createElement("div");
24061         el.className = this.itemCls;
24062         t.overwrite(el);
24063         container.dom.insertBefore(el, position);
24064         this.el = Roo.get(el);
24065         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24066         if(this.clickEvent != 'click'){
24067             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24068         }
24069     },
24070
24071     // private
24072     afterRender : function(){
24073         Roo.ColorPalette.superclass.afterRender.call(this);
24074         if(this.value){
24075             var s = this.value;
24076             this.value = null;
24077             this.select(s);
24078         }
24079     },
24080
24081     // private
24082     handleClick : function(e, t){
24083         e.preventDefault();
24084         if(!this.disabled){
24085             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24086             this.select(c.toUpperCase());
24087         }
24088     },
24089
24090     /**
24091      * Selects the specified color in the palette (fires the select event)
24092      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24093      */
24094     select : function(color){
24095         color = color.replace("#", "");
24096         if(color != this.value || this.allowReselect){
24097             var el = this.el;
24098             if(this.value){
24099                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24100             }
24101             el.child("a.color-"+color).addClass("x-color-palette-sel");
24102             this.value = color;
24103             this.fireEvent("select", this, color);
24104         }
24105     }
24106 });/*
24107  * Based on:
24108  * Ext JS Library 1.1.1
24109  * Copyright(c) 2006-2007, Ext JS, LLC.
24110  *
24111  * Originally Released Under LGPL - original licence link has changed is not relivant.
24112  *
24113  * Fork - LGPL
24114  * <script type="text/javascript">
24115  */
24116  
24117 /**
24118  * @class Roo.DatePicker
24119  * @extends Roo.Component
24120  * Simple date picker class.
24121  * @constructor
24122  * Create a new DatePicker
24123  * @param {Object} config The config object
24124  */
24125 Roo.DatePicker = function(config){
24126     Roo.DatePicker.superclass.constructor.call(this, config);
24127
24128     this.value = config && config.value ?
24129                  config.value.clearTime() : new Date().clearTime();
24130
24131     this.addEvents({
24132         /**
24133              * @event select
24134              * Fires when a date is selected
24135              * @param {DatePicker} this
24136              * @param {Date} date The selected date
24137              */
24138         select: true
24139     });
24140
24141     if(this.handler){
24142         this.on("select", this.handler,  this.scope || this);
24143     }
24144     // build the disabledDatesRE
24145     if(!this.disabledDatesRE && this.disabledDates){
24146         var dd = this.disabledDates;
24147         var re = "(?:";
24148         for(var i = 0; i < dd.length; i++){
24149             re += dd[i];
24150             if(i != dd.length-1) re += "|";
24151         }
24152         this.disabledDatesRE = new RegExp(re + ")");
24153     }
24154 };
24155
24156 Roo.extend(Roo.DatePicker, Roo.Component, {
24157     /**
24158      * @cfg {String} todayText
24159      * The text to display on the button that selects the current date (defaults to "Today")
24160      */
24161     todayText : "Today",
24162     /**
24163      * @cfg {String} okText
24164      * The text to display on the ok button
24165      */
24166     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24167     /**
24168      * @cfg {String} cancelText
24169      * The text to display on the cancel button
24170      */
24171     cancelText : "Cancel",
24172     /**
24173      * @cfg {String} todayTip
24174      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24175      */
24176     todayTip : "{0} (Spacebar)",
24177     /**
24178      * @cfg {Date} minDate
24179      * Minimum allowable date (JavaScript date object, defaults to null)
24180      */
24181     minDate : null,
24182     /**
24183      * @cfg {Date} maxDate
24184      * Maximum allowable date (JavaScript date object, defaults to null)
24185      */
24186     maxDate : null,
24187     /**
24188      * @cfg {String} minText
24189      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24190      */
24191     minText : "This date is before the minimum date",
24192     /**
24193      * @cfg {String} maxText
24194      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24195      */
24196     maxText : "This date is after the maximum date",
24197     /**
24198      * @cfg {String} format
24199      * The default date format string which can be overriden for localization support.  The format must be
24200      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24201      */
24202     format : "m/d/y",
24203     /**
24204      * @cfg {Array} disabledDays
24205      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24206      */
24207     disabledDays : null,
24208     /**
24209      * @cfg {String} disabledDaysText
24210      * The tooltip to display when the date falls on a disabled day (defaults to "")
24211      */
24212     disabledDaysText : "",
24213     /**
24214      * @cfg {RegExp} disabledDatesRE
24215      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24216      */
24217     disabledDatesRE : null,
24218     /**
24219      * @cfg {String} disabledDatesText
24220      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24221      */
24222     disabledDatesText : "",
24223     /**
24224      * @cfg {Boolean} constrainToViewport
24225      * True to constrain the date picker to the viewport (defaults to true)
24226      */
24227     constrainToViewport : true,
24228     /**
24229      * @cfg {Array} monthNames
24230      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24231      */
24232     monthNames : Date.monthNames,
24233     /**
24234      * @cfg {Array} dayNames
24235      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24236      */
24237     dayNames : Date.dayNames,
24238     /**
24239      * @cfg {String} nextText
24240      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24241      */
24242     nextText: 'Next Month (Control+Right)',
24243     /**
24244      * @cfg {String} prevText
24245      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24246      */
24247     prevText: 'Previous Month (Control+Left)',
24248     /**
24249      * @cfg {String} monthYearText
24250      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24251      */
24252     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24253     /**
24254      * @cfg {Number} startDay
24255      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24256      */
24257     startDay : 0,
24258     /**
24259      * @cfg {Bool} showClear
24260      * Show a clear button (usefull for date form elements that can be blank.)
24261      */
24262     
24263     showClear: false,
24264     
24265     /**
24266      * Sets the value of the date field
24267      * @param {Date} value The date to set
24268      */
24269     setValue : function(value){
24270         var old = this.value;
24271         this.value = value.clearTime(true);
24272         if(this.el){
24273             this.update(this.value);
24274         }
24275     },
24276
24277     /**
24278      * Gets the current selected value of the date field
24279      * @return {Date} The selected date
24280      */
24281     getValue : function(){
24282         return this.value;
24283     },
24284
24285     // private
24286     focus : function(){
24287         if(this.el){
24288             this.update(this.activeDate);
24289         }
24290     },
24291
24292     // private
24293     onRender : function(container, position){
24294         var m = [
24295              '<table cellspacing="0">',
24296                 '<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>',
24297                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24298         var dn = this.dayNames;
24299         for(var i = 0; i < 7; i++){
24300             var d = this.startDay+i;
24301             if(d > 6){
24302                 d = d-7;
24303             }
24304             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24305         }
24306         m[m.length] = "</tr></thead><tbody><tr>";
24307         for(var i = 0; i < 42; i++) {
24308             if(i % 7 == 0 && i != 0){
24309                 m[m.length] = "</tr><tr>";
24310             }
24311             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24312         }
24313         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24314             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24315
24316         var el = document.createElement("div");
24317         el.className = "x-date-picker";
24318         el.innerHTML = m.join("");
24319
24320         container.dom.insertBefore(el, position);
24321
24322         this.el = Roo.get(el);
24323         this.eventEl = Roo.get(el.firstChild);
24324
24325         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24326             handler: this.showPrevMonth,
24327             scope: this,
24328             preventDefault:true,
24329             stopDefault:true
24330         });
24331
24332         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24333             handler: this.showNextMonth,
24334             scope: this,
24335             preventDefault:true,
24336             stopDefault:true
24337         });
24338
24339         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24340
24341         this.monthPicker = this.el.down('div.x-date-mp');
24342         this.monthPicker.enableDisplayMode('block');
24343         
24344         var kn = new Roo.KeyNav(this.eventEl, {
24345             "left" : function(e){
24346                 e.ctrlKey ?
24347                     this.showPrevMonth() :
24348                     this.update(this.activeDate.add("d", -1));
24349             },
24350
24351             "right" : function(e){
24352                 e.ctrlKey ?
24353                     this.showNextMonth() :
24354                     this.update(this.activeDate.add("d", 1));
24355             },
24356
24357             "up" : function(e){
24358                 e.ctrlKey ?
24359                     this.showNextYear() :
24360                     this.update(this.activeDate.add("d", -7));
24361             },
24362
24363             "down" : function(e){
24364                 e.ctrlKey ?
24365                     this.showPrevYear() :
24366                     this.update(this.activeDate.add("d", 7));
24367             },
24368
24369             "pageUp" : function(e){
24370                 this.showNextMonth();
24371             },
24372
24373             "pageDown" : function(e){
24374                 this.showPrevMonth();
24375             },
24376
24377             "enter" : function(e){
24378                 e.stopPropagation();
24379                 return true;
24380             },
24381
24382             scope : this
24383         });
24384
24385         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24386
24387         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24388
24389         this.el.unselectable();
24390         
24391         this.cells = this.el.select("table.x-date-inner tbody td");
24392         this.textNodes = this.el.query("table.x-date-inner tbody span");
24393
24394         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24395             text: "&#160;",
24396             tooltip: this.monthYearText
24397         });
24398
24399         this.mbtn.on('click', this.showMonthPicker, this);
24400         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24401
24402
24403         var today = (new Date()).dateFormat(this.format);
24404         
24405         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24406         baseTb.add({
24407             text: String.format(this.todayText, today),
24408             tooltip: String.format(this.todayTip, today),
24409             handler: this.selectToday,
24410             scope: this
24411         });
24412         
24413         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24414             
24415         //});
24416         if (this.showClear) {
24417             
24418             baseTb.add( new Roo.Toolbar.Fill());
24419             baseTb.add({
24420                 text: '&#160;',
24421                 cls: 'x-btn-icon x-btn-clear',
24422                 handler: function() {
24423                     //this.value = '';
24424                     this.fireEvent("select", this, '');
24425                 },
24426                 scope: this
24427             });
24428         }
24429         
24430         
24431         if(Roo.isIE){
24432             this.el.repaint();
24433         }
24434         this.update(this.value);
24435     },
24436
24437     createMonthPicker : function(){
24438         if(!this.monthPicker.dom.firstChild){
24439             var buf = ['<table border="0" cellspacing="0">'];
24440             for(var i = 0; i < 6; i++){
24441                 buf.push(
24442                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24443                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24444                     i == 0 ?
24445                     '<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>' :
24446                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24447                 );
24448             }
24449             buf.push(
24450                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24451                     this.okText,
24452                     '</button><button type="button" class="x-date-mp-cancel">',
24453                     this.cancelText,
24454                     '</button></td></tr>',
24455                 '</table>'
24456             );
24457             this.monthPicker.update(buf.join(''));
24458             this.monthPicker.on('click', this.onMonthClick, this);
24459             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24460
24461             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24462             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24463
24464             this.mpMonths.each(function(m, a, i){
24465                 i += 1;
24466                 if((i%2) == 0){
24467                     m.dom.xmonth = 5 + Math.round(i * .5);
24468                 }else{
24469                     m.dom.xmonth = Math.round((i-1) * .5);
24470                 }
24471             });
24472         }
24473     },
24474
24475     showMonthPicker : function(){
24476         this.createMonthPicker();
24477         var size = this.el.getSize();
24478         this.monthPicker.setSize(size);
24479         this.monthPicker.child('table').setSize(size);
24480
24481         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24482         this.updateMPMonth(this.mpSelMonth);
24483         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24484         this.updateMPYear(this.mpSelYear);
24485
24486         this.monthPicker.slideIn('t', {duration:.2});
24487     },
24488
24489     updateMPYear : function(y){
24490         this.mpyear = y;
24491         var ys = this.mpYears.elements;
24492         for(var i = 1; i <= 10; i++){
24493             var td = ys[i-1], y2;
24494             if((i%2) == 0){
24495                 y2 = y + Math.round(i * .5);
24496                 td.firstChild.innerHTML = y2;
24497                 td.xyear = y2;
24498             }else{
24499                 y2 = y - (5-Math.round(i * .5));
24500                 td.firstChild.innerHTML = y2;
24501                 td.xyear = y2;
24502             }
24503             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24504         }
24505     },
24506
24507     updateMPMonth : function(sm){
24508         this.mpMonths.each(function(m, a, i){
24509             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24510         });
24511     },
24512
24513     selectMPMonth: function(m){
24514         
24515     },
24516
24517     onMonthClick : function(e, t){
24518         e.stopEvent();
24519         var el = new Roo.Element(t), pn;
24520         if(el.is('button.x-date-mp-cancel')){
24521             this.hideMonthPicker();
24522         }
24523         else if(el.is('button.x-date-mp-ok')){
24524             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24525             this.hideMonthPicker();
24526         }
24527         else if(pn = el.up('td.x-date-mp-month', 2)){
24528             this.mpMonths.removeClass('x-date-mp-sel');
24529             pn.addClass('x-date-mp-sel');
24530             this.mpSelMonth = pn.dom.xmonth;
24531         }
24532         else if(pn = el.up('td.x-date-mp-year', 2)){
24533             this.mpYears.removeClass('x-date-mp-sel');
24534             pn.addClass('x-date-mp-sel');
24535             this.mpSelYear = pn.dom.xyear;
24536         }
24537         else if(el.is('a.x-date-mp-prev')){
24538             this.updateMPYear(this.mpyear-10);
24539         }
24540         else if(el.is('a.x-date-mp-next')){
24541             this.updateMPYear(this.mpyear+10);
24542         }
24543     },
24544
24545     onMonthDblClick : function(e, t){
24546         e.stopEvent();
24547         var el = new Roo.Element(t), pn;
24548         if(pn = el.up('td.x-date-mp-month', 2)){
24549             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24550             this.hideMonthPicker();
24551         }
24552         else if(pn = el.up('td.x-date-mp-year', 2)){
24553             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24554             this.hideMonthPicker();
24555         }
24556     },
24557
24558     hideMonthPicker : function(disableAnim){
24559         if(this.monthPicker){
24560             if(disableAnim === true){
24561                 this.monthPicker.hide();
24562             }else{
24563                 this.monthPicker.slideOut('t', {duration:.2});
24564             }
24565         }
24566     },
24567
24568     // private
24569     showPrevMonth : function(e){
24570         this.update(this.activeDate.add("mo", -1));
24571     },
24572
24573     // private
24574     showNextMonth : function(e){
24575         this.update(this.activeDate.add("mo", 1));
24576     },
24577
24578     // private
24579     showPrevYear : function(){
24580         this.update(this.activeDate.add("y", -1));
24581     },
24582
24583     // private
24584     showNextYear : function(){
24585         this.update(this.activeDate.add("y", 1));
24586     },
24587
24588     // private
24589     handleMouseWheel : function(e){
24590         var delta = e.getWheelDelta();
24591         if(delta > 0){
24592             this.showPrevMonth();
24593             e.stopEvent();
24594         } else if(delta < 0){
24595             this.showNextMonth();
24596             e.stopEvent();
24597         }
24598     },
24599
24600     // private
24601     handleDateClick : function(e, t){
24602         e.stopEvent();
24603         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24604             this.setValue(new Date(t.dateValue));
24605             this.fireEvent("select", this, this.value);
24606         }
24607     },
24608
24609     // private
24610     selectToday : function(){
24611         this.setValue(new Date().clearTime());
24612         this.fireEvent("select", this, this.value);
24613     },
24614
24615     // private
24616     update : function(date){
24617         var vd = this.activeDate;
24618         this.activeDate = date;
24619         if(vd && this.el){
24620             var t = date.getTime();
24621             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24622                 this.cells.removeClass("x-date-selected");
24623                 this.cells.each(function(c){
24624                    if(c.dom.firstChild.dateValue == t){
24625                        c.addClass("x-date-selected");
24626                        setTimeout(function(){
24627                             try{c.dom.firstChild.focus();}catch(e){}
24628                        }, 50);
24629                        return false;
24630                    }
24631                 });
24632                 return;
24633             }
24634         }
24635         var days = date.getDaysInMonth();
24636         var firstOfMonth = date.getFirstDateOfMonth();
24637         var startingPos = firstOfMonth.getDay()-this.startDay;
24638
24639         if(startingPos <= this.startDay){
24640             startingPos += 7;
24641         }
24642
24643         var pm = date.add("mo", -1);
24644         var prevStart = pm.getDaysInMonth()-startingPos;
24645
24646         var cells = this.cells.elements;
24647         var textEls = this.textNodes;
24648         days += startingPos;
24649
24650         // convert everything to numbers so it's fast
24651         var day = 86400000;
24652         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24653         var today = new Date().clearTime().getTime();
24654         var sel = date.clearTime().getTime();
24655         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24656         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24657         var ddMatch = this.disabledDatesRE;
24658         var ddText = this.disabledDatesText;
24659         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24660         var ddaysText = this.disabledDaysText;
24661         var format = this.format;
24662
24663         var setCellClass = function(cal, cell){
24664             cell.title = "";
24665             var t = d.getTime();
24666             cell.firstChild.dateValue = t;
24667             if(t == today){
24668                 cell.className += " x-date-today";
24669                 cell.title = cal.todayText;
24670             }
24671             if(t == sel){
24672                 cell.className += " x-date-selected";
24673                 setTimeout(function(){
24674                     try{cell.firstChild.focus();}catch(e){}
24675                 }, 50);
24676             }
24677             // disabling
24678             if(t < min) {
24679                 cell.className = " x-date-disabled";
24680                 cell.title = cal.minText;
24681                 return;
24682             }
24683             if(t > max) {
24684                 cell.className = " x-date-disabled";
24685                 cell.title = cal.maxText;
24686                 return;
24687             }
24688             if(ddays){
24689                 if(ddays.indexOf(d.getDay()) != -1){
24690                     cell.title = ddaysText;
24691                     cell.className = " x-date-disabled";
24692                 }
24693             }
24694             if(ddMatch && format){
24695                 var fvalue = d.dateFormat(format);
24696                 if(ddMatch.test(fvalue)){
24697                     cell.title = ddText.replace("%0", fvalue);
24698                     cell.className = " x-date-disabled";
24699                 }
24700             }
24701         };
24702
24703         var i = 0;
24704         for(; i < startingPos; i++) {
24705             textEls[i].innerHTML = (++prevStart);
24706             d.setDate(d.getDate()+1);
24707             cells[i].className = "x-date-prevday";
24708             setCellClass(this, cells[i]);
24709         }
24710         for(; i < days; i++){
24711             intDay = i - startingPos + 1;
24712             textEls[i].innerHTML = (intDay);
24713             d.setDate(d.getDate()+1);
24714             cells[i].className = "x-date-active";
24715             setCellClass(this, cells[i]);
24716         }
24717         var extraDays = 0;
24718         for(; i < 42; i++) {
24719              textEls[i].innerHTML = (++extraDays);
24720              d.setDate(d.getDate()+1);
24721              cells[i].className = "x-date-nextday";
24722              setCellClass(this, cells[i]);
24723         }
24724
24725         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24726
24727         if(!this.internalRender){
24728             var main = this.el.dom.firstChild;
24729             var w = main.offsetWidth;
24730             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24731             Roo.fly(main).setWidth(w);
24732             this.internalRender = true;
24733             // opera does not respect the auto grow header center column
24734             // then, after it gets a width opera refuses to recalculate
24735             // without a second pass
24736             if(Roo.isOpera && !this.secondPass){
24737                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24738                 this.secondPass = true;
24739                 this.update.defer(10, this, [date]);
24740             }
24741         }
24742     }
24743 });/*
24744  * Based on:
24745  * Ext JS Library 1.1.1
24746  * Copyright(c) 2006-2007, Ext JS, LLC.
24747  *
24748  * Originally Released Under LGPL - original licence link has changed is not relivant.
24749  *
24750  * Fork - LGPL
24751  * <script type="text/javascript">
24752  */
24753 /**
24754  * @class Roo.TabPanel
24755  * @extends Roo.util.Observable
24756  * A lightweight tab container.
24757  * <br><br>
24758  * Usage:
24759  * <pre><code>
24760 // basic tabs 1, built from existing content
24761 var tabs = new Roo.TabPanel("tabs1");
24762 tabs.addTab("script", "View Script");
24763 tabs.addTab("markup", "View Markup");
24764 tabs.activate("script");
24765
24766 // more advanced tabs, built from javascript
24767 var jtabs = new Roo.TabPanel("jtabs");
24768 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24769
24770 // set up the UpdateManager
24771 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24772 var updater = tab2.getUpdateManager();
24773 updater.setDefaultUrl("ajax1.htm");
24774 tab2.on('activate', updater.refresh, updater, true);
24775
24776 // Use setUrl for Ajax loading
24777 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24778 tab3.setUrl("ajax2.htm", null, true);
24779
24780 // Disabled tab
24781 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24782 tab4.disable();
24783
24784 jtabs.activate("jtabs-1");
24785  * </code></pre>
24786  * @constructor
24787  * Create a new TabPanel.
24788  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24789  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24790  */
24791 Roo.TabPanel = function(container, config){
24792     /**
24793     * The container element for this TabPanel.
24794     * @type Roo.Element
24795     */
24796     this.el = Roo.get(container, true);
24797     if(config){
24798         if(typeof config == "boolean"){
24799             this.tabPosition = config ? "bottom" : "top";
24800         }else{
24801             Roo.apply(this, config);
24802         }
24803     }
24804     if(this.tabPosition == "bottom"){
24805         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24806         this.el.addClass("x-tabs-bottom");
24807     }
24808     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24809     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24810     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24811     if(Roo.isIE){
24812         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24813     }
24814     if(this.tabPosition != "bottom"){
24815     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24816      * @type Roo.Element
24817      */
24818       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24819       this.el.addClass("x-tabs-top");
24820     }
24821     this.items = [];
24822
24823     this.bodyEl.setStyle("position", "relative");
24824
24825     this.active = null;
24826     this.activateDelegate = this.activate.createDelegate(this);
24827
24828     this.addEvents({
24829         /**
24830          * @event tabchange
24831          * Fires when the active tab changes
24832          * @param {Roo.TabPanel} this
24833          * @param {Roo.TabPanelItem} activePanel The new active tab
24834          */
24835         "tabchange": true,
24836         /**
24837          * @event beforetabchange
24838          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24839          * @param {Roo.TabPanel} this
24840          * @param {Object} e Set cancel to true on this object to cancel the tab change
24841          * @param {Roo.TabPanelItem} tab The tab being changed to
24842          */
24843         "beforetabchange" : true
24844     });
24845
24846     Roo.EventManager.onWindowResize(this.onResize, this);
24847     this.cpad = this.el.getPadding("lr");
24848     this.hiddenCount = 0;
24849
24850     Roo.TabPanel.superclass.constructor.call(this);
24851 };
24852
24853 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24854         /*
24855          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24856          */
24857     tabPosition : "top",
24858         /*
24859          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24860          */
24861     currentTabWidth : 0,
24862         /*
24863          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24864          */
24865     minTabWidth : 40,
24866         /*
24867          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24868          */
24869     maxTabWidth : 250,
24870         /*
24871          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24872          */
24873     preferredTabWidth : 175,
24874         /*
24875          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24876          */
24877     resizeTabs : false,
24878         /*
24879          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24880          */
24881     monitorResize : true,
24882
24883     /**
24884      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24885      * @param {String} id The id of the div to use <b>or create</b>
24886      * @param {String} text The text for the tab
24887      * @param {String} content (optional) Content to put in the TabPanelItem body
24888      * @param {Boolean} closable (optional) True to create a close icon on the tab
24889      * @return {Roo.TabPanelItem} The created TabPanelItem
24890      */
24891     addTab : function(id, text, content, closable){
24892         var item = new Roo.TabPanelItem(this, id, text, closable);
24893         this.addTabItem(item);
24894         if(content){
24895             item.setContent(content);
24896         }
24897         return item;
24898     },
24899
24900     /**
24901      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24902      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24903      * @return {Roo.TabPanelItem}
24904      */
24905     getTab : function(id){
24906         return this.items[id];
24907     },
24908
24909     /**
24910      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24911      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24912      */
24913     hideTab : function(id){
24914         var t = this.items[id];
24915         if(!t.isHidden()){
24916            t.setHidden(true);
24917            this.hiddenCount++;
24918            this.autoSizeTabs();
24919         }
24920     },
24921
24922     /**
24923      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
24924      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
24925      */
24926     unhideTab : function(id){
24927         var t = this.items[id];
24928         if(t.isHidden()){
24929            t.setHidden(false);
24930            this.hiddenCount--;
24931            this.autoSizeTabs();
24932         }
24933     },
24934
24935     /**
24936      * Adds an existing {@link Roo.TabPanelItem}.
24937      * @param {Roo.TabPanelItem} item The TabPanelItem to add
24938      */
24939     addTabItem : function(item){
24940         this.items[item.id] = item;
24941         this.items.push(item);
24942         if(this.resizeTabs){
24943            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
24944            this.autoSizeTabs();
24945         }else{
24946             item.autoSize();
24947         }
24948     },
24949
24950     /**
24951      * Removes a {@link Roo.TabPanelItem}.
24952      * @param {String/Number} id The id or index of the TabPanelItem to remove.
24953      */
24954     removeTab : function(id){
24955         var items = this.items;
24956         var tab = items[id];
24957         if(!tab) return;
24958         var index = items.indexOf(tab);
24959         if(this.active == tab && items.length > 1){
24960             var newTab = this.getNextAvailable(index);
24961             if(newTab)newTab.activate();
24962         }
24963         this.stripEl.dom.removeChild(tab.pnode.dom);
24964         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
24965             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
24966         }
24967         items.splice(index, 1);
24968         delete this.items[tab.id];
24969         tab.fireEvent("close", tab);
24970         tab.purgeListeners();
24971         this.autoSizeTabs();
24972     },
24973
24974     getNextAvailable : function(start){
24975         var items = this.items;
24976         var index = start;
24977         // look for a next tab that will slide over to
24978         // replace the one being removed
24979         while(index < items.length){
24980             var item = items[++index];
24981             if(item && !item.isHidden()){
24982                 return item;
24983             }
24984         }
24985         // if one isn't found select the previous tab (on the left)
24986         index = start;
24987         while(index >= 0){
24988             var item = items[--index];
24989             if(item && !item.isHidden()){
24990                 return item;
24991             }
24992         }
24993         return null;
24994     },
24995
24996     /**
24997      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
24998      * @param {String/Number} id The id or index of the TabPanelItem to disable.
24999      */
25000     disableTab : function(id){
25001         var tab = this.items[id];
25002         if(tab && this.active != tab){
25003             tab.disable();
25004         }
25005     },
25006
25007     /**
25008      * Enables a {@link Roo.TabPanelItem} that is disabled.
25009      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25010      */
25011     enableTab : function(id){
25012         var tab = this.items[id];
25013         tab.enable();
25014     },
25015
25016     /**
25017      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25018      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25019      * @return {Roo.TabPanelItem} The TabPanelItem.
25020      */
25021     activate : function(id){
25022         var tab = this.items[id];
25023         if(!tab){
25024             return null;
25025         }
25026         if(tab == this.active || tab.disabled){
25027             return tab;
25028         }
25029         var e = {};
25030         this.fireEvent("beforetabchange", this, e, tab);
25031         if(e.cancel !== true && !tab.disabled){
25032             if(this.active){
25033                 this.active.hide();
25034             }
25035             this.active = this.items[id];
25036             this.active.show();
25037             this.fireEvent("tabchange", this, this.active);
25038         }
25039         return tab;
25040     },
25041
25042     /**
25043      * Gets the active {@link Roo.TabPanelItem}.
25044      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25045      */
25046     getActiveTab : function(){
25047         return this.active;
25048     },
25049
25050     /**
25051      * Updates the tab body element to fit the height of the container element
25052      * for overflow scrolling
25053      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25054      */
25055     syncHeight : function(targetHeight){
25056         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25057         var bm = this.bodyEl.getMargins();
25058         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25059         this.bodyEl.setHeight(newHeight);
25060         return newHeight;
25061     },
25062
25063     onResize : function(){
25064         if(this.monitorResize){
25065             this.autoSizeTabs();
25066         }
25067     },
25068
25069     /**
25070      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25071      */
25072     beginUpdate : function(){
25073         this.updating = true;
25074     },
25075
25076     /**
25077      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25078      */
25079     endUpdate : function(){
25080         this.updating = false;
25081         this.autoSizeTabs();
25082     },
25083
25084     /**
25085      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25086      */
25087     autoSizeTabs : function(){
25088         var count = this.items.length;
25089         var vcount = count - this.hiddenCount;
25090         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25091         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25092         var availWidth = Math.floor(w / vcount);
25093         var b = this.stripBody;
25094         if(b.getWidth() > w){
25095             var tabs = this.items;
25096             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25097             if(availWidth < this.minTabWidth){
25098                 /*if(!this.sleft){    // incomplete scrolling code
25099                     this.createScrollButtons();
25100                 }
25101                 this.showScroll();
25102                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25103             }
25104         }else{
25105             if(this.currentTabWidth < this.preferredTabWidth){
25106                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25107             }
25108         }
25109     },
25110
25111     /**
25112      * Returns the number of tabs in this TabPanel.
25113      * @return {Number}
25114      */
25115      getCount : function(){
25116          return this.items.length;
25117      },
25118
25119     /**
25120      * Resizes all the tabs to the passed width
25121      * @param {Number} The new width
25122      */
25123     setTabWidth : function(width){
25124         this.currentTabWidth = width;
25125         for(var i = 0, len = this.items.length; i < len; i++) {
25126                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25127         }
25128     },
25129
25130     /**
25131      * Destroys this TabPanel
25132      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25133      */
25134     destroy : function(removeEl){
25135         Roo.EventManager.removeResizeListener(this.onResize, this);
25136         for(var i = 0, len = this.items.length; i < len; i++){
25137             this.items[i].purgeListeners();
25138         }
25139         if(removeEl === true){
25140             this.el.update("");
25141             this.el.remove();
25142         }
25143     }
25144 });
25145
25146 /**
25147  * @class Roo.TabPanelItem
25148  * @extends Roo.util.Observable
25149  * Represents an individual item (tab plus body) in a TabPanel.
25150  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25151  * @param {String} id The id of this TabPanelItem
25152  * @param {String} text The text for the tab of this TabPanelItem
25153  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25154  */
25155 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25156     /**
25157      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25158      * @type Roo.TabPanel
25159      */
25160     this.tabPanel = tabPanel;
25161     /**
25162      * The id for this TabPanelItem
25163      * @type String
25164      */
25165     this.id = id;
25166     /** @private */
25167     this.disabled = false;
25168     /** @private */
25169     this.text = text;
25170     /** @private */
25171     this.loaded = false;
25172     this.closable = closable;
25173
25174     /**
25175      * The body element for this TabPanelItem.
25176      * @type Roo.Element
25177      */
25178     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25179     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25180     this.bodyEl.setStyle("display", "block");
25181     this.bodyEl.setStyle("zoom", "1");
25182     this.hideAction();
25183
25184     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25185     /** @private */
25186     this.el = Roo.get(els.el, true);
25187     this.inner = Roo.get(els.inner, true);
25188     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25189     this.pnode = Roo.get(els.el.parentNode, true);
25190     this.el.on("mousedown", this.onTabMouseDown, this);
25191     this.el.on("click", this.onTabClick, this);
25192     /** @private */
25193     if(closable){
25194         var c = Roo.get(els.close, true);
25195         c.dom.title = this.closeText;
25196         c.addClassOnOver("close-over");
25197         c.on("click", this.closeClick, this);
25198      }
25199
25200     this.addEvents({
25201          /**
25202          * @event activate
25203          * Fires when this tab becomes the active tab.
25204          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25205          * @param {Roo.TabPanelItem} this
25206          */
25207         "activate": true,
25208         /**
25209          * @event beforeclose
25210          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25211          * @param {Roo.TabPanelItem} this
25212          * @param {Object} e Set cancel to true on this object to cancel the close.
25213          */
25214         "beforeclose": true,
25215         /**
25216          * @event close
25217          * Fires when this tab is closed.
25218          * @param {Roo.TabPanelItem} this
25219          */
25220          "close": true,
25221         /**
25222          * @event deactivate
25223          * Fires when this tab is no longer the active tab.
25224          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25225          * @param {Roo.TabPanelItem} this
25226          */
25227          "deactivate" : true
25228     });
25229     this.hidden = false;
25230
25231     Roo.TabPanelItem.superclass.constructor.call(this);
25232 };
25233
25234 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25235     purgeListeners : function(){
25236        Roo.util.Observable.prototype.purgeListeners.call(this);
25237        this.el.removeAllListeners();
25238     },
25239     /**
25240      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25241      */
25242     show : function(){
25243         this.pnode.addClass("on");
25244         this.showAction();
25245         if(Roo.isOpera){
25246             this.tabPanel.stripWrap.repaint();
25247         }
25248         this.fireEvent("activate", this.tabPanel, this);
25249     },
25250
25251     /**
25252      * Returns true if this tab is the active tab.
25253      * @return {Boolean}
25254      */
25255     isActive : function(){
25256         return this.tabPanel.getActiveTab() == this;
25257     },
25258
25259     /**
25260      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25261      */
25262     hide : function(){
25263         this.pnode.removeClass("on");
25264         this.hideAction();
25265         this.fireEvent("deactivate", this.tabPanel, this);
25266     },
25267
25268     hideAction : function(){
25269         this.bodyEl.hide();
25270         this.bodyEl.setStyle("position", "absolute");
25271         this.bodyEl.setLeft("-20000px");
25272         this.bodyEl.setTop("-20000px");
25273     },
25274
25275     showAction : function(){
25276         this.bodyEl.setStyle("position", "relative");
25277         this.bodyEl.setTop("");
25278         this.bodyEl.setLeft("");
25279         this.bodyEl.show();
25280     },
25281
25282     /**
25283      * Set the tooltip for the tab.
25284      * @param {String} tooltip The tab's tooltip
25285      */
25286     setTooltip : function(text){
25287         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25288             this.textEl.dom.qtip = text;
25289             this.textEl.dom.removeAttribute('title');
25290         }else{
25291             this.textEl.dom.title = text;
25292         }
25293     },
25294
25295     onTabClick : function(e){
25296         e.preventDefault();
25297         this.tabPanel.activate(this.id);
25298     },
25299
25300     onTabMouseDown : function(e){
25301         e.preventDefault();
25302         this.tabPanel.activate(this.id);
25303     },
25304
25305     getWidth : function(){
25306         return this.inner.getWidth();
25307     },
25308
25309     setWidth : function(width){
25310         var iwidth = width - this.pnode.getPadding("lr");
25311         this.inner.setWidth(iwidth);
25312         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25313         this.pnode.setWidth(width);
25314     },
25315
25316     /**
25317      * Show or hide the tab
25318      * @param {Boolean} hidden True to hide or false to show.
25319      */
25320     setHidden : function(hidden){
25321         this.hidden = hidden;
25322         this.pnode.setStyle("display", hidden ? "none" : "");
25323     },
25324
25325     /**
25326      * Returns true if this tab is "hidden"
25327      * @return {Boolean}
25328      */
25329     isHidden : function(){
25330         return this.hidden;
25331     },
25332
25333     /**
25334      * Returns the text for this tab
25335      * @return {String}
25336      */
25337     getText : function(){
25338         return this.text;
25339     },
25340
25341     autoSize : function(){
25342         //this.el.beginMeasure();
25343         this.textEl.setWidth(1);
25344         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25345         //this.el.endMeasure();
25346     },
25347
25348     /**
25349      * Sets the text for the tab (Note: this also sets the tooltip text)
25350      * @param {String} text The tab's text and tooltip
25351      */
25352     setText : function(text){
25353         this.text = text;
25354         this.textEl.update(text);
25355         this.setTooltip(text);
25356         if(!this.tabPanel.resizeTabs){
25357             this.autoSize();
25358         }
25359     },
25360     /**
25361      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25362      */
25363     activate : function(){
25364         this.tabPanel.activate(this.id);
25365     },
25366
25367     /**
25368      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25369      */
25370     disable : function(){
25371         if(this.tabPanel.active != this){
25372             this.disabled = true;
25373             this.pnode.addClass("disabled");
25374         }
25375     },
25376
25377     /**
25378      * Enables this TabPanelItem if it was previously disabled.
25379      */
25380     enable : function(){
25381         this.disabled = false;
25382         this.pnode.removeClass("disabled");
25383     },
25384
25385     /**
25386      * Sets the content for this TabPanelItem.
25387      * @param {String} content The content
25388      * @param {Boolean} loadScripts true to look for and load scripts
25389      */
25390     setContent : function(content, loadScripts){
25391         this.bodyEl.update(content, loadScripts);
25392     },
25393
25394     /**
25395      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25396      * @return {Roo.UpdateManager} The UpdateManager
25397      */
25398     getUpdateManager : function(){
25399         return this.bodyEl.getUpdateManager();
25400     },
25401
25402     /**
25403      * Set a URL to be used to load the content for this TabPanelItem.
25404      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25405      * @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)
25406      * @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)
25407      * @return {Roo.UpdateManager} The UpdateManager
25408      */
25409     setUrl : function(url, params, loadOnce){
25410         if(this.refreshDelegate){
25411             this.un('activate', this.refreshDelegate);
25412         }
25413         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25414         this.on("activate", this.refreshDelegate);
25415         return this.bodyEl.getUpdateManager();
25416     },
25417
25418     /** @private */
25419     _handleRefresh : function(url, params, loadOnce){
25420         if(!loadOnce || !this.loaded){
25421             var updater = this.bodyEl.getUpdateManager();
25422             updater.update(url, params, this._setLoaded.createDelegate(this));
25423         }
25424     },
25425
25426     /**
25427      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25428      *   Will fail silently if the setUrl method has not been called.
25429      *   This does not activate the panel, just updates its content.
25430      */
25431     refresh : function(){
25432         if(this.refreshDelegate){
25433            this.loaded = false;
25434            this.refreshDelegate();
25435         }
25436     },
25437
25438     /** @private */
25439     _setLoaded : function(){
25440         this.loaded = true;
25441     },
25442
25443     /** @private */
25444     closeClick : function(e){
25445         var o = {};
25446         e.stopEvent();
25447         this.fireEvent("beforeclose", this, o);
25448         if(o.cancel !== true){
25449             this.tabPanel.removeTab(this.id);
25450         }
25451     },
25452     /**
25453      * The text displayed in the tooltip for the close icon.
25454      * @type String
25455      */
25456     closeText : "Close this tab"
25457 });
25458
25459 /** @private */
25460 Roo.TabPanel.prototype.createStrip = function(container){
25461     var strip = document.createElement("div");
25462     strip.className = "x-tabs-wrap";
25463     container.appendChild(strip);
25464     return strip;
25465 };
25466 /** @private */
25467 Roo.TabPanel.prototype.createStripList = function(strip){
25468     // div wrapper for retard IE
25469     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25470     return strip.firstChild.firstChild.firstChild.firstChild;
25471 };
25472 /** @private */
25473 Roo.TabPanel.prototype.createBody = function(container){
25474     var body = document.createElement("div");
25475     Roo.id(body, "tab-body");
25476     Roo.fly(body).addClass("x-tabs-body");
25477     container.appendChild(body);
25478     return body;
25479 };
25480 /** @private */
25481 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25482     var body = Roo.getDom(id);
25483     if(!body){
25484         body = document.createElement("div");
25485         body.id = id;
25486     }
25487     Roo.fly(body).addClass("x-tabs-item-body");
25488     bodyEl.insertBefore(body, bodyEl.firstChild);
25489     return body;
25490 };
25491 /** @private */
25492 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25493     var td = document.createElement("td");
25494     stripEl.appendChild(td);
25495     if(closable){
25496         td.className = "x-tabs-closable";
25497         if(!this.closeTpl){
25498             this.closeTpl = new Roo.Template(
25499                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25500                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25501                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25502             );
25503         }
25504         var el = this.closeTpl.overwrite(td, {"text": text});
25505         var close = el.getElementsByTagName("div")[0];
25506         var inner = el.getElementsByTagName("em")[0];
25507         return {"el": el, "close": close, "inner": inner};
25508     } else {
25509         if(!this.tabTpl){
25510             this.tabTpl = new Roo.Template(
25511                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25512                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25513             );
25514         }
25515         var el = this.tabTpl.overwrite(td, {"text": text});
25516         var inner = el.getElementsByTagName("em")[0];
25517         return {"el": el, "inner": inner};
25518     }
25519 };/*
25520  * Based on:
25521  * Ext JS Library 1.1.1
25522  * Copyright(c) 2006-2007, Ext JS, LLC.
25523  *
25524  * Originally Released Under LGPL - original licence link has changed is not relivant.
25525  *
25526  * Fork - LGPL
25527  * <script type="text/javascript">
25528  */
25529
25530 /**
25531  * @class Roo.Button
25532  * @extends Roo.util.Observable
25533  * Simple Button class
25534  * @cfg {String} text The button text
25535  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25536  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25537  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25538  * @cfg {Object} scope The scope of the handler
25539  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25540  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25541  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25542  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25543  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25544  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25545    applies if enableToggle = true)
25546  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25547  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25548   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25549  * @constructor
25550  * Create a new button
25551  * @param {Object} config The config object
25552  */
25553 Roo.Button = function(renderTo, config)
25554 {
25555     if (!config) {
25556         config = renderTo;
25557         renderTo = config.renderTo || false;
25558     }
25559     
25560     Roo.apply(this, config);
25561     this.addEvents({
25562         /**
25563              * @event click
25564              * Fires when this button is clicked
25565              * @param {Button} this
25566              * @param {EventObject} e The click event
25567              */
25568             "click" : true,
25569         /**
25570              * @event toggle
25571              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25572              * @param {Button} this
25573              * @param {Boolean} pressed
25574              */
25575             "toggle" : true,
25576         /**
25577              * @event mouseover
25578              * Fires when the mouse hovers over the button
25579              * @param {Button} this
25580              * @param {Event} e The event object
25581              */
25582         'mouseover' : true,
25583         /**
25584              * @event mouseout
25585              * Fires when the mouse exits the button
25586              * @param {Button} this
25587              * @param {Event} e The event object
25588              */
25589         'mouseout': true,
25590          /**
25591              * @event render
25592              * Fires when the button is rendered
25593              * @param {Button} this
25594              */
25595         'render': true
25596     });
25597     if(this.menu){
25598         this.menu = Roo.menu.MenuMgr.get(this.menu);
25599     }
25600     if(renderTo){
25601         this.render(renderTo);
25602     }
25603     
25604     Roo.util.Observable.call(this);
25605 };
25606
25607 Roo.extend(Roo.Button, Roo.util.Observable, {
25608     /**
25609      * 
25610      */
25611     
25612     /**
25613      * Read-only. True if this button is hidden
25614      * @type Boolean
25615      */
25616     hidden : false,
25617     /**
25618      * Read-only. True if this button is disabled
25619      * @type Boolean
25620      */
25621     disabled : false,
25622     /**
25623      * Read-only. True if this button is pressed (only if enableToggle = true)
25624      * @type Boolean
25625      */
25626     pressed : false,
25627
25628     /**
25629      * @cfg {Number} tabIndex 
25630      * The DOM tabIndex for this button (defaults to undefined)
25631      */
25632     tabIndex : undefined,
25633
25634     /**
25635      * @cfg {Boolean} enableToggle
25636      * True to enable pressed/not pressed toggling (defaults to false)
25637      */
25638     enableToggle: false,
25639     /**
25640      * @cfg {Mixed} menu
25641      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25642      */
25643     menu : undefined,
25644     /**
25645      * @cfg {String} menuAlign
25646      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25647      */
25648     menuAlign : "tl-bl?",
25649
25650     /**
25651      * @cfg {String} iconCls
25652      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25653      */
25654     iconCls : undefined,
25655     /**
25656      * @cfg {String} type
25657      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25658      */
25659     type : 'button',
25660
25661     // private
25662     menuClassTarget: 'tr',
25663
25664     /**
25665      * @cfg {String} clickEvent
25666      * The type of event to map to the button's event handler (defaults to 'click')
25667      */
25668     clickEvent : 'click',
25669
25670     /**
25671      * @cfg {Boolean} handleMouseEvents
25672      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25673      */
25674     handleMouseEvents : true,
25675
25676     /**
25677      * @cfg {String} tooltipType
25678      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25679      */
25680     tooltipType : 'qtip',
25681
25682     /**
25683      * @cfg {String} cls
25684      * A CSS class to apply to the button's main element.
25685      */
25686     
25687     /**
25688      * @cfg {Roo.Template} template (Optional)
25689      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25690      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25691      * require code modifications if required elements (e.g. a button) aren't present.
25692      */
25693
25694     // private
25695     render : function(renderTo){
25696         var btn;
25697         if(this.hideParent){
25698             this.parentEl = Roo.get(renderTo);
25699         }
25700         if(!this.dhconfig){
25701             if(!this.template){
25702                 if(!Roo.Button.buttonTemplate){
25703                     // hideous table template
25704                     Roo.Button.buttonTemplate = new Roo.Template(
25705                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25706                         '<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>',
25707                         "</tr></tbody></table>");
25708                 }
25709                 this.template = Roo.Button.buttonTemplate;
25710             }
25711             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25712             var btnEl = btn.child("button:first");
25713             btnEl.on('focus', this.onFocus, this);
25714             btnEl.on('blur', this.onBlur, this);
25715             if(this.cls){
25716                 btn.addClass(this.cls);
25717             }
25718             if(this.icon){
25719                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25720             }
25721             if(this.iconCls){
25722                 btnEl.addClass(this.iconCls);
25723                 if(!this.cls){
25724                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25725                 }
25726             }
25727             if(this.tabIndex !== undefined){
25728                 btnEl.dom.tabIndex = this.tabIndex;
25729             }
25730             if(this.tooltip){
25731                 if(typeof this.tooltip == 'object'){
25732                     Roo.QuickTips.tips(Roo.apply({
25733                           target: btnEl.id
25734                     }, this.tooltip));
25735                 } else {
25736                     btnEl.dom[this.tooltipType] = this.tooltip;
25737                 }
25738             }
25739         }else{
25740             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25741         }
25742         this.el = btn;
25743         if(this.id){
25744             this.el.dom.id = this.el.id = this.id;
25745         }
25746         if(this.menu){
25747             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25748             this.menu.on("show", this.onMenuShow, this);
25749             this.menu.on("hide", this.onMenuHide, this);
25750         }
25751         btn.addClass("x-btn");
25752         if(Roo.isIE && !Roo.isIE7){
25753             this.autoWidth.defer(1, this);
25754         }else{
25755             this.autoWidth();
25756         }
25757         if(this.handleMouseEvents){
25758             btn.on("mouseover", this.onMouseOver, this);
25759             btn.on("mouseout", this.onMouseOut, this);
25760             btn.on("mousedown", this.onMouseDown, this);
25761         }
25762         btn.on(this.clickEvent, this.onClick, this);
25763         //btn.on("mouseup", this.onMouseUp, this);
25764         if(this.hidden){
25765             this.hide();
25766         }
25767         if(this.disabled){
25768             this.disable();
25769         }
25770         Roo.ButtonToggleMgr.register(this);
25771         if(this.pressed){
25772             this.el.addClass("x-btn-pressed");
25773         }
25774         if(this.repeat){
25775             var repeater = new Roo.util.ClickRepeater(btn,
25776                 typeof this.repeat == "object" ? this.repeat : {}
25777             );
25778             repeater.on("click", this.onClick,  this);
25779         }
25780         this.fireEvent('render', this);
25781         
25782     },
25783     /**
25784      * Returns the button's underlying element
25785      * @return {Roo.Element} The element
25786      */
25787     getEl : function(){
25788         return this.el;  
25789     },
25790     
25791     /**
25792      * Destroys this Button and removes any listeners.
25793      */
25794     destroy : function(){
25795         Roo.ButtonToggleMgr.unregister(this);
25796         this.el.removeAllListeners();
25797         this.purgeListeners();
25798         this.el.remove();
25799     },
25800
25801     // private
25802     autoWidth : function(){
25803         if(this.el){
25804             this.el.setWidth("auto");
25805             if(Roo.isIE7 && Roo.isStrict){
25806                 var ib = this.el.child('button');
25807                 if(ib && ib.getWidth() > 20){
25808                     ib.clip();
25809                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25810                 }
25811             }
25812             if(this.minWidth){
25813                 if(this.hidden){
25814                     this.el.beginMeasure();
25815                 }
25816                 if(this.el.getWidth() < this.minWidth){
25817                     this.el.setWidth(this.minWidth);
25818                 }
25819                 if(this.hidden){
25820                     this.el.endMeasure();
25821                 }
25822             }
25823         }
25824     },
25825
25826     /**
25827      * Assigns this button's click handler
25828      * @param {Function} handler The function to call when the button is clicked
25829      * @param {Object} scope (optional) Scope for the function passed in
25830      */
25831     setHandler : function(handler, scope){
25832         this.handler = handler;
25833         this.scope = scope;  
25834     },
25835     
25836     /**
25837      * Sets this button's text
25838      * @param {String} text The button text
25839      */
25840     setText : function(text){
25841         this.text = text;
25842         if(this.el){
25843             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25844         }
25845         this.autoWidth();
25846     },
25847     
25848     /**
25849      * Gets the text for this button
25850      * @return {String} The button text
25851      */
25852     getText : function(){
25853         return this.text;  
25854     },
25855     
25856     /**
25857      * Show this button
25858      */
25859     show: function(){
25860         this.hidden = false;
25861         if(this.el){
25862             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25863         }
25864     },
25865     
25866     /**
25867      * Hide this button
25868      */
25869     hide: function(){
25870         this.hidden = true;
25871         if(this.el){
25872             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25873         }
25874     },
25875     
25876     /**
25877      * Convenience function for boolean show/hide
25878      * @param {Boolean} visible True to show, false to hide
25879      */
25880     setVisible: function(visible){
25881         if(visible) {
25882             this.show();
25883         }else{
25884             this.hide();
25885         }
25886     },
25887     
25888     /**
25889      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25890      * @param {Boolean} state (optional) Force a particular state
25891      */
25892     toggle : function(state){
25893         state = state === undefined ? !this.pressed : state;
25894         if(state != this.pressed){
25895             if(state){
25896                 this.el.addClass("x-btn-pressed");
25897                 this.pressed = true;
25898                 this.fireEvent("toggle", this, true);
25899             }else{
25900                 this.el.removeClass("x-btn-pressed");
25901                 this.pressed = false;
25902                 this.fireEvent("toggle", this, false);
25903             }
25904             if(this.toggleHandler){
25905                 this.toggleHandler.call(this.scope || this, this, state);
25906             }
25907         }
25908     },
25909     
25910     /**
25911      * Focus the button
25912      */
25913     focus : function(){
25914         this.el.child('button:first').focus();
25915     },
25916     
25917     /**
25918      * Disable this button
25919      */
25920     disable : function(){
25921         if(this.el){
25922             this.el.addClass("x-btn-disabled");
25923         }
25924         this.disabled = true;
25925     },
25926     
25927     /**
25928      * Enable this button
25929      */
25930     enable : function(){
25931         if(this.el){
25932             this.el.removeClass("x-btn-disabled");
25933         }
25934         this.disabled = false;
25935     },
25936
25937     /**
25938      * Convenience function for boolean enable/disable
25939      * @param {Boolean} enabled True to enable, false to disable
25940      */
25941     setDisabled : function(v){
25942         this[v !== true ? "enable" : "disable"]();
25943     },
25944
25945     // private
25946     onClick : function(e){
25947         if(e){
25948             e.preventDefault();
25949         }
25950         if(e.button != 0){
25951             return;
25952         }
25953         if(!this.disabled){
25954             if(this.enableToggle){
25955                 this.toggle();
25956             }
25957             if(this.menu && !this.menu.isVisible()){
25958                 this.menu.show(this.el, this.menuAlign);
25959             }
25960             this.fireEvent("click", this, e);
25961             if(this.handler){
25962                 this.el.removeClass("x-btn-over");
25963                 this.handler.call(this.scope || this, this, e);
25964             }
25965         }
25966     },
25967     // private
25968     onMouseOver : function(e){
25969         if(!this.disabled){
25970             this.el.addClass("x-btn-over");
25971             this.fireEvent('mouseover', this, e);
25972         }
25973     },
25974     // private
25975     onMouseOut : function(e){
25976         if(!e.within(this.el,  true)){
25977             this.el.removeClass("x-btn-over");
25978             this.fireEvent('mouseout', this, e);
25979         }
25980     },
25981     // private
25982     onFocus : function(e){
25983         if(!this.disabled){
25984             this.el.addClass("x-btn-focus");
25985         }
25986     },
25987     // private
25988     onBlur : function(e){
25989         this.el.removeClass("x-btn-focus");
25990     },
25991     // private
25992     onMouseDown : function(e){
25993         if(!this.disabled && e.button == 0){
25994             this.el.addClass("x-btn-click");
25995             Roo.get(document).on('mouseup', this.onMouseUp, this);
25996         }
25997     },
25998     // private
25999     onMouseUp : function(e){
26000         if(e.button == 0){
26001             this.el.removeClass("x-btn-click");
26002             Roo.get(document).un('mouseup', this.onMouseUp, this);
26003         }
26004     },
26005     // private
26006     onMenuShow : function(e){
26007         this.el.addClass("x-btn-menu-active");
26008     },
26009     // private
26010     onMenuHide : function(e){
26011         this.el.removeClass("x-btn-menu-active");
26012     }   
26013 });
26014
26015 // Private utility class used by Button
26016 Roo.ButtonToggleMgr = function(){
26017    var groups = {};
26018    
26019    function toggleGroup(btn, state){
26020        if(state){
26021            var g = groups[btn.toggleGroup];
26022            for(var i = 0, l = g.length; i < l; i++){
26023                if(g[i] != btn){
26024                    g[i].toggle(false);
26025                }
26026            }
26027        }
26028    }
26029    
26030    return {
26031        register : function(btn){
26032            if(!btn.toggleGroup){
26033                return;
26034            }
26035            var g = groups[btn.toggleGroup];
26036            if(!g){
26037                g = groups[btn.toggleGroup] = [];
26038            }
26039            g.push(btn);
26040            btn.on("toggle", toggleGroup);
26041        },
26042        
26043        unregister : function(btn){
26044            if(!btn.toggleGroup){
26045                return;
26046            }
26047            var g = groups[btn.toggleGroup];
26048            if(g){
26049                g.remove(btn);
26050                btn.un("toggle", toggleGroup);
26051            }
26052        }
26053    };
26054 }();/*
26055  * Based on:
26056  * Ext JS Library 1.1.1
26057  * Copyright(c) 2006-2007, Ext JS, LLC.
26058  *
26059  * Originally Released Under LGPL - original licence link has changed is not relivant.
26060  *
26061  * Fork - LGPL
26062  * <script type="text/javascript">
26063  */
26064  
26065 /**
26066  * @class Roo.SplitButton
26067  * @extends Roo.Button
26068  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26069  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26070  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26071  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26072  * @cfg {String} arrowTooltip The title attribute of the arrow
26073  * @constructor
26074  * Create a new menu button
26075  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26076  * @param {Object} config The config object
26077  */
26078 Roo.SplitButton = function(renderTo, config){
26079     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26080     /**
26081      * @event arrowclick
26082      * Fires when this button's arrow is clicked
26083      * @param {SplitButton} this
26084      * @param {EventObject} e The click event
26085      */
26086     this.addEvents({"arrowclick":true});
26087 };
26088
26089 Roo.extend(Roo.SplitButton, Roo.Button, {
26090     render : function(renderTo){
26091         // this is one sweet looking template!
26092         var tpl = new Roo.Template(
26093             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26094             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26095             '<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>',
26096             "</tbody></table></td><td>",
26097             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26098             '<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>',
26099             "</tbody></table></td></tr></table>"
26100         );
26101         var btn = tpl.append(renderTo, [this.text, this.type], true);
26102         var btnEl = btn.child("button");
26103         if(this.cls){
26104             btn.addClass(this.cls);
26105         }
26106         if(this.icon){
26107             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26108         }
26109         if(this.iconCls){
26110             btnEl.addClass(this.iconCls);
26111             if(!this.cls){
26112                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26113             }
26114         }
26115         this.el = btn;
26116         if(this.handleMouseEvents){
26117             btn.on("mouseover", this.onMouseOver, this);
26118             btn.on("mouseout", this.onMouseOut, this);
26119             btn.on("mousedown", this.onMouseDown, this);
26120             btn.on("mouseup", this.onMouseUp, this);
26121         }
26122         btn.on(this.clickEvent, this.onClick, this);
26123         if(this.tooltip){
26124             if(typeof this.tooltip == 'object'){
26125                 Roo.QuickTips.tips(Roo.apply({
26126                       target: btnEl.id
26127                 }, this.tooltip));
26128             } else {
26129                 btnEl.dom[this.tooltipType] = this.tooltip;
26130             }
26131         }
26132         if(this.arrowTooltip){
26133             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26134         }
26135         if(this.hidden){
26136             this.hide();
26137         }
26138         if(this.disabled){
26139             this.disable();
26140         }
26141         if(this.pressed){
26142             this.el.addClass("x-btn-pressed");
26143         }
26144         if(Roo.isIE && !Roo.isIE7){
26145             this.autoWidth.defer(1, this);
26146         }else{
26147             this.autoWidth();
26148         }
26149         if(this.menu){
26150             this.menu.on("show", this.onMenuShow, this);
26151             this.menu.on("hide", this.onMenuHide, this);
26152         }
26153         this.fireEvent('render', this);
26154     },
26155
26156     // private
26157     autoWidth : function(){
26158         if(this.el){
26159             var tbl = this.el.child("table:first");
26160             var tbl2 = this.el.child("table:last");
26161             this.el.setWidth("auto");
26162             tbl.setWidth("auto");
26163             if(Roo.isIE7 && Roo.isStrict){
26164                 var ib = this.el.child('button:first');
26165                 if(ib && ib.getWidth() > 20){
26166                     ib.clip();
26167                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26168                 }
26169             }
26170             if(this.minWidth){
26171                 if(this.hidden){
26172                     this.el.beginMeasure();
26173                 }
26174                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26175                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26176                 }
26177                 if(this.hidden){
26178                     this.el.endMeasure();
26179                 }
26180             }
26181             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26182         } 
26183     },
26184     /**
26185      * Sets this button's click handler
26186      * @param {Function} handler The function to call when the button is clicked
26187      * @param {Object} scope (optional) Scope for the function passed above
26188      */
26189     setHandler : function(handler, scope){
26190         this.handler = handler;
26191         this.scope = scope;  
26192     },
26193     
26194     /**
26195      * Sets this button's arrow click handler
26196      * @param {Function} handler The function to call when the arrow is clicked
26197      * @param {Object} scope (optional) Scope for the function passed above
26198      */
26199     setArrowHandler : function(handler, scope){
26200         this.arrowHandler = handler;
26201         this.scope = scope;  
26202     },
26203     
26204     /**
26205      * Focus the button
26206      */
26207     focus : function(){
26208         if(this.el){
26209             this.el.child("button:first").focus();
26210         }
26211     },
26212
26213     // private
26214     onClick : function(e){
26215         e.preventDefault();
26216         if(!this.disabled){
26217             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26218                 if(this.menu && !this.menu.isVisible()){
26219                     this.menu.show(this.el, this.menuAlign);
26220                 }
26221                 this.fireEvent("arrowclick", this, e);
26222                 if(this.arrowHandler){
26223                     this.arrowHandler.call(this.scope || this, this, e);
26224                 }
26225             }else{
26226                 this.fireEvent("click", this, e);
26227                 if(this.handler){
26228                     this.handler.call(this.scope || this, this, e);
26229                 }
26230             }
26231         }
26232     },
26233     // private
26234     onMouseDown : function(e){
26235         if(!this.disabled){
26236             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26237         }
26238     },
26239     // private
26240     onMouseUp : function(e){
26241         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26242     }   
26243 });
26244
26245
26246 // backwards compat
26247 Roo.MenuButton = Roo.SplitButton;/*
26248  * Based on:
26249  * Ext JS Library 1.1.1
26250  * Copyright(c) 2006-2007, Ext JS, LLC.
26251  *
26252  * Originally Released Under LGPL - original licence link has changed is not relivant.
26253  *
26254  * Fork - LGPL
26255  * <script type="text/javascript">
26256  */
26257
26258 /**
26259  * @class Roo.Toolbar
26260  * Basic Toolbar class.
26261  * @constructor
26262  * Creates a new Toolbar
26263  * @param {Object} config The config object
26264  */ 
26265 Roo.Toolbar = function(container, buttons, config)
26266 {
26267     /// old consturctor format still supported..
26268     if(container instanceof Array){ // omit the container for later rendering
26269         buttons = container;
26270         config = buttons;
26271         container = null;
26272     }
26273     if (typeof(container) == 'object' && container.xtype) {
26274         config = container;
26275         container = config.container;
26276         buttons = config.buttons; // not really - use items!!
26277     }
26278     var xitems = [];
26279     if (config && config.items) {
26280         xitems = config.items;
26281         delete config.items;
26282     }
26283     Roo.apply(this, config);
26284     this.buttons = buttons;
26285     
26286     if(container){
26287         this.render(container);
26288     }
26289     Roo.each(xitems, function(b) {
26290         this.add(b);
26291     }, this);
26292     
26293 };
26294
26295 Roo.Toolbar.prototype = {
26296     /**
26297      * @cfg {Roo.data.Store} items
26298      * array of button configs or elements to add
26299      */
26300     
26301     /**
26302      * @cfg {String/HTMLElement/Element} container
26303      * The id or element that will contain the toolbar
26304      */
26305     // private
26306     render : function(ct){
26307         this.el = Roo.get(ct);
26308         if(this.cls){
26309             this.el.addClass(this.cls);
26310         }
26311         // using a table allows for vertical alignment
26312         // 100% width is needed by Safari...
26313         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26314         this.tr = this.el.child("tr", true);
26315         var autoId = 0;
26316         this.items = new Roo.util.MixedCollection(false, function(o){
26317             return o.id || ("item" + (++autoId));
26318         });
26319         if(this.buttons){
26320             this.add.apply(this, this.buttons);
26321             delete this.buttons;
26322         }
26323     },
26324
26325     /**
26326      * Adds element(s) to the toolbar -- this function takes a variable number of 
26327      * arguments of mixed type and adds them to the toolbar.
26328      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26329      * <ul>
26330      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26331      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26332      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26333      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26334      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26335      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26336      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26337      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26338      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26339      * </ul>
26340      * @param {Mixed} arg2
26341      * @param {Mixed} etc.
26342      */
26343     add : function(){
26344         var a = arguments, l = a.length;
26345         for(var i = 0; i < l; i++){
26346             this._add(a[i]);
26347         }
26348     },
26349     // private..
26350     _add : function(el) {
26351         
26352         if (el.xtype) {
26353             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26354         }
26355         
26356         if (el.applyTo){ // some kind of form field
26357             return this.addField(el);
26358         } 
26359         if (el.render){ // some kind of Toolbar.Item
26360             return this.addItem(el);
26361         }
26362         if (typeof el == "string"){ // string
26363             if(el == "separator" || el == "-"){
26364                 return this.addSeparator();
26365             }
26366             if (el == " "){
26367                 return this.addSpacer();
26368             }
26369             if(el == "->"){
26370                 return this.addFill();
26371             }
26372             return this.addText(el);
26373             
26374         }
26375         if(el.tagName){ // element
26376             return this.addElement(el);
26377         }
26378         if(typeof el == "object"){ // must be button config?
26379             return this.addButton(el);
26380         }
26381         // and now what?!?!
26382         return false;
26383         
26384     },
26385     
26386     /**
26387      * Add an Xtype element
26388      * @param {Object} xtype Xtype Object
26389      * @return {Object} created Object
26390      */
26391     addxtype : function(e){
26392         return this.add(e);  
26393     },
26394     
26395     /**
26396      * Returns the Element for this toolbar.
26397      * @return {Roo.Element}
26398      */
26399     getEl : function(){
26400         return this.el;  
26401     },
26402     
26403     /**
26404      * Adds a separator
26405      * @return {Roo.Toolbar.Item} The separator item
26406      */
26407     addSeparator : function(){
26408         return this.addItem(new Roo.Toolbar.Separator());
26409     },
26410
26411     /**
26412      * Adds a spacer element
26413      * @return {Roo.Toolbar.Spacer} The spacer item
26414      */
26415     addSpacer : function(){
26416         return this.addItem(new Roo.Toolbar.Spacer());
26417     },
26418
26419     /**
26420      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26421      * @return {Roo.Toolbar.Fill} The fill item
26422      */
26423     addFill : function(){
26424         return this.addItem(new Roo.Toolbar.Fill());
26425     },
26426
26427     /**
26428      * Adds any standard HTML element to the toolbar
26429      * @param {String/HTMLElement/Element} el The element or id of the element to add
26430      * @return {Roo.Toolbar.Item} The element's item
26431      */
26432     addElement : function(el){
26433         return this.addItem(new Roo.Toolbar.Item(el));
26434     },
26435     /**
26436      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26437      * @type Roo.util.MixedCollection  
26438      */
26439     items : false,
26440      
26441     /**
26442      * Adds any Toolbar.Item or subclass
26443      * @param {Roo.Toolbar.Item} item
26444      * @return {Roo.Toolbar.Item} The item
26445      */
26446     addItem : function(item){
26447         var td = this.nextBlock();
26448         item.render(td);
26449         this.items.add(item);
26450         return item;
26451     },
26452     
26453     /**
26454      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26455      * @param {Object/Array} config A button config or array of configs
26456      * @return {Roo.Toolbar.Button/Array}
26457      */
26458     addButton : function(config){
26459         if(config instanceof Array){
26460             var buttons = [];
26461             for(var i = 0, len = config.length; i < len; i++) {
26462                 buttons.push(this.addButton(config[i]));
26463             }
26464             return buttons;
26465         }
26466         var b = config;
26467         if(!(config instanceof Roo.Toolbar.Button)){
26468             b = config.split ?
26469                 new Roo.Toolbar.SplitButton(config) :
26470                 new Roo.Toolbar.Button(config);
26471         }
26472         var td = this.nextBlock();
26473         b.render(td);
26474         this.items.add(b);
26475         return b;
26476     },
26477     
26478     /**
26479      * Adds text to the toolbar
26480      * @param {String} text The text to add
26481      * @return {Roo.Toolbar.Item} The element's item
26482      */
26483     addText : function(text){
26484         return this.addItem(new Roo.Toolbar.TextItem(text));
26485     },
26486     
26487     /**
26488      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26489      * @param {Number} index The index where the item is to be inserted
26490      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26491      * @return {Roo.Toolbar.Button/Item}
26492      */
26493     insertButton : function(index, item){
26494         if(item instanceof Array){
26495             var buttons = [];
26496             for(var i = 0, len = item.length; i < len; i++) {
26497                buttons.push(this.insertButton(index + i, item[i]));
26498             }
26499             return buttons;
26500         }
26501         if (!(item instanceof Roo.Toolbar.Button)){
26502            item = new Roo.Toolbar.Button(item);
26503         }
26504         var td = document.createElement("td");
26505         this.tr.insertBefore(td, this.tr.childNodes[index]);
26506         item.render(td);
26507         this.items.insert(index, item);
26508         return item;
26509     },
26510     
26511     /**
26512      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26513      * @param {Object} config
26514      * @return {Roo.Toolbar.Item} The element's item
26515      */
26516     addDom : function(config, returnEl){
26517         var td = this.nextBlock();
26518         Roo.DomHelper.overwrite(td, config);
26519         var ti = new Roo.Toolbar.Item(td.firstChild);
26520         ti.render(td);
26521         this.items.add(ti);
26522         return ti;
26523     },
26524
26525     /**
26526      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26527      * @type Roo.util.MixedCollection  
26528      */
26529     fields : false,
26530     
26531     /**
26532      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26533      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26534      * @param {Roo.form.Field} field
26535      * @return {Roo.ToolbarItem}
26536      */
26537      
26538       
26539     addField : function(field) {
26540         if (!this.fields) {
26541             var autoId = 0;
26542             this.fields = new Roo.util.MixedCollection(false, function(o){
26543                 return o.id || ("item" + (++autoId));
26544             });
26545
26546         }
26547         
26548         var td = this.nextBlock();
26549         field.render(td);
26550         var ti = new Roo.Toolbar.Item(td.firstChild);
26551         ti.render(td);
26552         this.items.add(ti);
26553         this.fields.add(field);
26554         return ti;
26555     },
26556     /**
26557      * Hide the toolbar
26558      * @method hide
26559      */
26560      
26561       
26562     hide : function()
26563     {
26564         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26565         this.el.child('div').hide();
26566     },
26567     /**
26568      * Show the toolbar
26569      * @method show
26570      */
26571     show : function()
26572     {
26573         this.el.child('div').show();
26574     },
26575       
26576     // private
26577     nextBlock : function(){
26578         var td = document.createElement("td");
26579         this.tr.appendChild(td);
26580         return td;
26581     },
26582
26583     // private
26584     destroy : function(){
26585         if(this.items){ // rendered?
26586             Roo.destroy.apply(Roo, this.items.items);
26587         }
26588         if(this.fields){ // rendered?
26589             Roo.destroy.apply(Roo, this.fields.items);
26590         }
26591         Roo.Element.uncache(this.el, this.tr);
26592     }
26593 };
26594
26595 /**
26596  * @class Roo.Toolbar.Item
26597  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26598  * @constructor
26599  * Creates a new Item
26600  * @param {HTMLElement} el 
26601  */
26602 Roo.Toolbar.Item = function(el){
26603     this.el = Roo.getDom(el);
26604     this.id = Roo.id(this.el);
26605     this.hidden = false;
26606 };
26607
26608 Roo.Toolbar.Item.prototype = {
26609     
26610     /**
26611      * Get this item's HTML Element
26612      * @return {HTMLElement}
26613      */
26614     getEl : function(){
26615        return this.el;  
26616     },
26617
26618     // private
26619     render : function(td){
26620         this.td = td;
26621         td.appendChild(this.el);
26622     },
26623     
26624     /**
26625      * Removes and destroys this item.
26626      */
26627     destroy : function(){
26628         this.td.parentNode.removeChild(this.td);
26629     },
26630     
26631     /**
26632      * Shows this item.
26633      */
26634     show: function(){
26635         this.hidden = false;
26636         this.td.style.display = "";
26637     },
26638     
26639     /**
26640      * Hides this item.
26641      */
26642     hide: function(){
26643         this.hidden = true;
26644         this.td.style.display = "none";
26645     },
26646     
26647     /**
26648      * Convenience function for boolean show/hide.
26649      * @param {Boolean} visible true to show/false to hide
26650      */
26651     setVisible: function(visible){
26652         if(visible) {
26653             this.show();
26654         }else{
26655             this.hide();
26656         }
26657     },
26658     
26659     /**
26660      * Try to focus this item.
26661      */
26662     focus : function(){
26663         Roo.fly(this.el).focus();
26664     },
26665     
26666     /**
26667      * Disables this item.
26668      */
26669     disable : function(){
26670         Roo.fly(this.td).addClass("x-item-disabled");
26671         this.disabled = true;
26672         this.el.disabled = true;
26673     },
26674     
26675     /**
26676      * Enables this item.
26677      */
26678     enable : function(){
26679         Roo.fly(this.td).removeClass("x-item-disabled");
26680         this.disabled = false;
26681         this.el.disabled = false;
26682     }
26683 };
26684
26685
26686 /**
26687  * @class Roo.Toolbar.Separator
26688  * @extends Roo.Toolbar.Item
26689  * A simple toolbar separator class
26690  * @constructor
26691  * Creates a new Separator
26692  */
26693 Roo.Toolbar.Separator = function(){
26694     var s = document.createElement("span");
26695     s.className = "ytb-sep";
26696     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26697 };
26698 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26699     enable:Roo.emptyFn,
26700     disable:Roo.emptyFn,
26701     focus:Roo.emptyFn
26702 });
26703
26704 /**
26705  * @class Roo.Toolbar.Spacer
26706  * @extends Roo.Toolbar.Item
26707  * A simple element that adds extra horizontal space to a toolbar.
26708  * @constructor
26709  * Creates a new Spacer
26710  */
26711 Roo.Toolbar.Spacer = function(){
26712     var s = document.createElement("div");
26713     s.className = "ytb-spacer";
26714     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26715 };
26716 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26717     enable:Roo.emptyFn,
26718     disable:Roo.emptyFn,
26719     focus:Roo.emptyFn
26720 });
26721
26722 /**
26723  * @class Roo.Toolbar.Fill
26724  * @extends Roo.Toolbar.Spacer
26725  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26726  * @constructor
26727  * Creates a new Spacer
26728  */
26729 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26730     // private
26731     render : function(td){
26732         td.style.width = '100%';
26733         Roo.Toolbar.Fill.superclass.render.call(this, td);
26734     }
26735 });
26736
26737 /**
26738  * @class Roo.Toolbar.TextItem
26739  * @extends Roo.Toolbar.Item
26740  * A simple class that renders text directly into a toolbar.
26741  * @constructor
26742  * Creates a new TextItem
26743  * @param {String} text
26744  */
26745 Roo.Toolbar.TextItem = function(text){
26746     if (typeof(text) == 'object') {
26747         text = text.text;
26748     }
26749     var s = document.createElement("span");
26750     s.className = "ytb-text";
26751     s.innerHTML = text;
26752     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26753 };
26754 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26755     enable:Roo.emptyFn,
26756     disable:Roo.emptyFn,
26757     focus:Roo.emptyFn
26758 });
26759
26760 /**
26761  * @class Roo.Toolbar.Button
26762  * @extends Roo.Button
26763  * A button that renders into a toolbar.
26764  * @constructor
26765  * Creates a new Button
26766  * @param {Object} config A standard {@link Roo.Button} config object
26767  */
26768 Roo.Toolbar.Button = function(config){
26769     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26770 };
26771 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26772     render : function(td){
26773         this.td = td;
26774         Roo.Toolbar.Button.superclass.render.call(this, td);
26775     },
26776     
26777     /**
26778      * Removes and destroys this button
26779      */
26780     destroy : function(){
26781         Roo.Toolbar.Button.superclass.destroy.call(this);
26782         this.td.parentNode.removeChild(this.td);
26783     },
26784     
26785     /**
26786      * Shows this button
26787      */
26788     show: function(){
26789         this.hidden = false;
26790         this.td.style.display = "";
26791     },
26792     
26793     /**
26794      * Hides this button
26795      */
26796     hide: function(){
26797         this.hidden = true;
26798         this.td.style.display = "none";
26799     },
26800
26801     /**
26802      * Disables this item
26803      */
26804     disable : function(){
26805         Roo.fly(this.td).addClass("x-item-disabled");
26806         this.disabled = true;
26807     },
26808
26809     /**
26810      * Enables this item
26811      */
26812     enable : function(){
26813         Roo.fly(this.td).removeClass("x-item-disabled");
26814         this.disabled = false;
26815     }
26816 });
26817 // backwards compat
26818 Roo.ToolbarButton = Roo.Toolbar.Button;
26819
26820 /**
26821  * @class Roo.Toolbar.SplitButton
26822  * @extends Roo.SplitButton
26823  * A menu button that renders into a toolbar.
26824  * @constructor
26825  * Creates a new SplitButton
26826  * @param {Object} config A standard {@link Roo.SplitButton} config object
26827  */
26828 Roo.Toolbar.SplitButton = function(config){
26829     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26830 };
26831 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26832     render : function(td){
26833         this.td = td;
26834         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26835     },
26836     
26837     /**
26838      * Removes and destroys this button
26839      */
26840     destroy : function(){
26841         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26842         this.td.parentNode.removeChild(this.td);
26843     },
26844     
26845     /**
26846      * Shows this button
26847      */
26848     show: function(){
26849         this.hidden = false;
26850         this.td.style.display = "";
26851     },
26852     
26853     /**
26854      * Hides this button
26855      */
26856     hide: function(){
26857         this.hidden = true;
26858         this.td.style.display = "none";
26859     }
26860 });
26861
26862 // backwards compat
26863 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26864  * Based on:
26865  * Ext JS Library 1.1.1
26866  * Copyright(c) 2006-2007, Ext JS, LLC.
26867  *
26868  * Originally Released Under LGPL - original licence link has changed is not relivant.
26869  *
26870  * Fork - LGPL
26871  * <script type="text/javascript">
26872  */
26873  
26874 /**
26875  * @class Roo.PagingToolbar
26876  * @extends Roo.Toolbar
26877  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26878  * @constructor
26879  * Create a new PagingToolbar
26880  * @param {Object} config The config object
26881  */
26882 Roo.PagingToolbar = function(el, ds, config)
26883 {
26884     // old args format still supported... - xtype is prefered..
26885     if (typeof(el) == 'object' && el.xtype) {
26886         // created from xtype...
26887         config = el;
26888         ds = el.dataSource;
26889         el = config.container;
26890     }
26891     
26892     
26893     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26894     this.ds = ds;
26895     this.cursor = 0;
26896     this.renderButtons(this.el);
26897     this.bind(ds);
26898 };
26899
26900 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26901     /**
26902      * @cfg {Roo.data.Store} dataSource
26903      * The underlying data store providing the paged data
26904      */
26905     /**
26906      * @cfg {String/HTMLElement/Element} container
26907      * container The id or element that will contain the toolbar
26908      */
26909     /**
26910      * @cfg {Boolean} displayInfo
26911      * True to display the displayMsg (defaults to false)
26912      */
26913     /**
26914      * @cfg {Number} pageSize
26915      * The number of records to display per page (defaults to 20)
26916      */
26917     pageSize: 20,
26918     /**
26919      * @cfg {String} displayMsg
26920      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26921      */
26922     displayMsg : 'Displaying {0} - {1} of {2}',
26923     /**
26924      * @cfg {String} emptyMsg
26925      * The message to display when no records are found (defaults to "No data to display")
26926      */
26927     emptyMsg : 'No data to display',
26928     /**
26929      * Customizable piece of the default paging text (defaults to "Page")
26930      * @type String
26931      */
26932     beforePageText : "Page",
26933     /**
26934      * Customizable piece of the default paging text (defaults to "of %0")
26935      * @type String
26936      */
26937     afterPageText : "of {0}",
26938     /**
26939      * Customizable piece of the default paging text (defaults to "First Page")
26940      * @type String
26941      */
26942     firstText : "First Page",
26943     /**
26944      * Customizable piece of the default paging text (defaults to "Previous Page")
26945      * @type String
26946      */
26947     prevText : "Previous Page",
26948     /**
26949      * Customizable piece of the default paging text (defaults to "Next Page")
26950      * @type String
26951      */
26952     nextText : "Next Page",
26953     /**
26954      * Customizable piece of the default paging text (defaults to "Last Page")
26955      * @type String
26956      */
26957     lastText : "Last Page",
26958     /**
26959      * Customizable piece of the default paging text (defaults to "Refresh")
26960      * @type String
26961      */
26962     refreshText : "Refresh",
26963
26964     // private
26965     renderButtons : function(el){
26966         Roo.PagingToolbar.superclass.render.call(this, el);
26967         this.first = this.addButton({
26968             tooltip: this.firstText,
26969             cls: "x-btn-icon x-grid-page-first",
26970             disabled: true,
26971             handler: this.onClick.createDelegate(this, ["first"])
26972         });
26973         this.prev = this.addButton({
26974             tooltip: this.prevText,
26975             cls: "x-btn-icon x-grid-page-prev",
26976             disabled: true,
26977             handler: this.onClick.createDelegate(this, ["prev"])
26978         });
26979         this.addSeparator();
26980         this.add(this.beforePageText);
26981         this.field = Roo.get(this.addDom({
26982            tag: "input",
26983            type: "text",
26984            size: "3",
26985            value: "1",
26986            cls: "x-grid-page-number"
26987         }).el);
26988         this.field.on("keydown", this.onPagingKeydown, this);
26989         this.field.on("focus", function(){this.dom.select();});
26990         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
26991         this.field.setHeight(18);
26992         this.addSeparator();
26993         this.next = this.addButton({
26994             tooltip: this.nextText,
26995             cls: "x-btn-icon x-grid-page-next",
26996             disabled: true,
26997             handler: this.onClick.createDelegate(this, ["next"])
26998         });
26999         this.last = this.addButton({
27000             tooltip: this.lastText,
27001             cls: "x-btn-icon x-grid-page-last",
27002             disabled: true,
27003             handler: this.onClick.createDelegate(this, ["last"])
27004         });
27005         this.addSeparator();
27006         this.loading = this.addButton({
27007             tooltip: this.refreshText,
27008             cls: "x-btn-icon x-grid-loading",
27009             handler: this.onClick.createDelegate(this, ["refresh"])
27010         });
27011
27012         if(this.displayInfo){
27013             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27014         }
27015     },
27016
27017     // private
27018     updateInfo : function(){
27019         if(this.displayEl){
27020             var count = this.ds.getCount();
27021             var msg = count == 0 ?
27022                 this.emptyMsg :
27023                 String.format(
27024                     this.displayMsg,
27025                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27026                 );
27027             this.displayEl.update(msg);
27028         }
27029     },
27030
27031     // private
27032     onLoad : function(ds, r, o){
27033        this.cursor = o.params ? o.params.start : 0;
27034        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27035
27036        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27037        this.field.dom.value = ap;
27038        this.first.setDisabled(ap == 1);
27039        this.prev.setDisabled(ap == 1);
27040        this.next.setDisabled(ap == ps);
27041        this.last.setDisabled(ap == ps);
27042        this.loading.enable();
27043        this.updateInfo();
27044     },
27045
27046     // private
27047     getPageData : function(){
27048         var total = this.ds.getTotalCount();
27049         return {
27050             total : total,
27051             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27052             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27053         };
27054     },
27055
27056     // private
27057     onLoadError : function(){
27058         this.loading.enable();
27059     },
27060
27061     // private
27062     onPagingKeydown : function(e){
27063         var k = e.getKey();
27064         var d = this.getPageData();
27065         if(k == e.RETURN){
27066             var v = this.field.dom.value, pageNum;
27067             if(!v || isNaN(pageNum = parseInt(v, 10))){
27068                 this.field.dom.value = d.activePage;
27069                 return;
27070             }
27071             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27072             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27073             e.stopEvent();
27074         }
27075         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))
27076         {
27077           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27078           this.field.dom.value = pageNum;
27079           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27080           e.stopEvent();
27081         }
27082         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27083         {
27084           var v = this.field.dom.value, pageNum; 
27085           var increment = (e.shiftKey) ? 10 : 1;
27086           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27087             increment *= -1;
27088           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27089             this.field.dom.value = d.activePage;
27090             return;
27091           }
27092           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27093           {
27094             this.field.dom.value = parseInt(v, 10) + increment;
27095             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27096             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27097           }
27098           e.stopEvent();
27099         }
27100     },
27101
27102     // private
27103     beforeLoad : function(){
27104         if(this.loading){
27105             this.loading.disable();
27106         }
27107     },
27108
27109     // private
27110     onClick : function(which){
27111         var ds = this.ds;
27112         switch(which){
27113             case "first":
27114                 ds.load({params:{start: 0, limit: this.pageSize}});
27115             break;
27116             case "prev":
27117                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27118             break;
27119             case "next":
27120                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27121             break;
27122             case "last":
27123                 var total = ds.getTotalCount();
27124                 var extra = total % this.pageSize;
27125                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27126                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27127             break;
27128             case "refresh":
27129                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27130             break;
27131         }
27132     },
27133
27134     /**
27135      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27136      * @param {Roo.data.Store} store The data store to unbind
27137      */
27138     unbind : function(ds){
27139         ds.un("beforeload", this.beforeLoad, this);
27140         ds.un("load", this.onLoad, this);
27141         ds.un("loadexception", this.onLoadError, this);
27142         ds.un("remove", this.updateInfo, this);
27143         ds.un("add", this.updateInfo, this);
27144         this.ds = undefined;
27145     },
27146
27147     /**
27148      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27149      * @param {Roo.data.Store} store The data store to bind
27150      */
27151     bind : function(ds){
27152         ds.on("beforeload", this.beforeLoad, this);
27153         ds.on("load", this.onLoad, this);
27154         ds.on("loadexception", this.onLoadError, this);
27155         ds.on("remove", this.updateInfo, this);
27156         ds.on("add", this.updateInfo, this);
27157         this.ds = ds;
27158     }
27159 });/*
27160  * Based on:
27161  * Ext JS Library 1.1.1
27162  * Copyright(c) 2006-2007, Ext JS, LLC.
27163  *
27164  * Originally Released Under LGPL - original licence link has changed is not relivant.
27165  *
27166  * Fork - LGPL
27167  * <script type="text/javascript">
27168  */
27169
27170 /**
27171  * @class Roo.Resizable
27172  * @extends Roo.util.Observable
27173  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27174  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27175  * 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
27176  * the element will be wrapped for you automatically.</p>
27177  * <p>Here is the list of valid resize handles:</p>
27178  * <pre>
27179 Value   Description
27180 ------  -------------------
27181  'n'     north
27182  's'     south
27183  'e'     east
27184  'w'     west
27185  'nw'    northwest
27186  'sw'    southwest
27187  'se'    southeast
27188  'ne'    northeast
27189  'all'   all
27190 </pre>
27191  * <p>Here's an example showing the creation of a typical Resizable:</p>
27192  * <pre><code>
27193 var resizer = new Roo.Resizable("element-id", {
27194     handles: 'all',
27195     minWidth: 200,
27196     minHeight: 100,
27197     maxWidth: 500,
27198     maxHeight: 400,
27199     pinned: true
27200 });
27201 resizer.on("resize", myHandler);
27202 </code></pre>
27203  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27204  * resizer.east.setDisplayed(false);</p>
27205  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27206  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27207  * resize operation's new size (defaults to [0, 0])
27208  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27209  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27210  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27211  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27212  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27213  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27214  * @cfg {Number} width The width of the element in pixels (defaults to null)
27215  * @cfg {Number} height The height of the element in pixels (defaults to null)
27216  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27217  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27218  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27219  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27220  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27221  * in favor of the handles config option (defaults to false)
27222  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27223  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27224  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27225  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27226  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27227  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27228  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27229  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27230  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27231  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27232  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27233  * @constructor
27234  * Create a new resizable component
27235  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27236  * @param {Object} config configuration options
27237   */
27238 Roo.Resizable = function(el, config){
27239     this.el = Roo.get(el);
27240
27241     if(config && config.wrap){
27242         config.resizeChild = this.el;
27243         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27244         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27245         this.el.setStyle("overflow", "hidden");
27246         this.el.setPositioning(config.resizeChild.getPositioning());
27247         config.resizeChild.clearPositioning();
27248         if(!config.width || !config.height){
27249             var csize = config.resizeChild.getSize();
27250             this.el.setSize(csize.width, csize.height);
27251         }
27252         if(config.pinned && !config.adjustments){
27253             config.adjustments = "auto";
27254         }
27255     }
27256
27257     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27258     this.proxy.unselectable();
27259     this.proxy.enableDisplayMode('block');
27260
27261     Roo.apply(this, config);
27262
27263     if(this.pinned){
27264         this.disableTrackOver = true;
27265         this.el.addClass("x-resizable-pinned");
27266     }
27267     // if the element isn't positioned, make it relative
27268     var position = this.el.getStyle("position");
27269     if(position != "absolute" && position != "fixed"){
27270         this.el.setStyle("position", "relative");
27271     }
27272     if(!this.handles){ // no handles passed, must be legacy style
27273         this.handles = 's,e,se';
27274         if(this.multiDirectional){
27275             this.handles += ',n,w';
27276         }
27277     }
27278     if(this.handles == "all"){
27279         this.handles = "n s e w ne nw se sw";
27280     }
27281     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27282     var ps = Roo.Resizable.positions;
27283     for(var i = 0, len = hs.length; i < len; i++){
27284         if(hs[i] && ps[hs[i]]){
27285             var pos = ps[hs[i]];
27286             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27287         }
27288     }
27289     // legacy
27290     this.corner = this.southeast;
27291
27292     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27293         this.updateBox = true;
27294     }
27295
27296     this.activeHandle = null;
27297
27298     if(this.resizeChild){
27299         if(typeof this.resizeChild == "boolean"){
27300             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27301         }else{
27302             this.resizeChild = Roo.get(this.resizeChild, true);
27303         }
27304     }
27305
27306     if(this.adjustments == "auto"){
27307         var rc = this.resizeChild;
27308         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27309         if(rc && (hw || hn)){
27310             rc.position("relative");
27311             rc.setLeft(hw ? hw.el.getWidth() : 0);
27312             rc.setTop(hn ? hn.el.getHeight() : 0);
27313         }
27314         this.adjustments = [
27315             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27316             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27317         ];
27318     }
27319
27320     if(this.draggable){
27321         this.dd = this.dynamic ?
27322             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27323         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27324     }
27325
27326     // public events
27327     this.addEvents({
27328         /**
27329          * @event beforeresize
27330          * Fired before resize is allowed. Set enabled to false to cancel resize.
27331          * @param {Roo.Resizable} this
27332          * @param {Roo.EventObject} e The mousedown event
27333          */
27334         "beforeresize" : true,
27335         /**
27336          * @event resize
27337          * Fired after a resize.
27338          * @param {Roo.Resizable} this
27339          * @param {Number} width The new width
27340          * @param {Number} height The new height
27341          * @param {Roo.EventObject} e The mouseup event
27342          */
27343         "resize" : true
27344     });
27345
27346     if(this.width !== null && this.height !== null){
27347         this.resizeTo(this.width, this.height);
27348     }else{
27349         this.updateChildSize();
27350     }
27351     if(Roo.isIE){
27352         this.el.dom.style.zoom = 1;
27353     }
27354     Roo.Resizable.superclass.constructor.call(this);
27355 };
27356
27357 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27358         resizeChild : false,
27359         adjustments : [0, 0],
27360         minWidth : 5,
27361         minHeight : 5,
27362         maxWidth : 10000,
27363         maxHeight : 10000,
27364         enabled : true,
27365         animate : false,
27366         duration : .35,
27367         dynamic : false,
27368         handles : false,
27369         multiDirectional : false,
27370         disableTrackOver : false,
27371         easing : 'easeOutStrong',
27372         widthIncrement : 0,
27373         heightIncrement : 0,
27374         pinned : false,
27375         width : null,
27376         height : null,
27377         preserveRatio : false,
27378         transparent: false,
27379         minX: 0,
27380         minY: 0,
27381         draggable: false,
27382
27383         /**
27384          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27385          */
27386         constrainTo: undefined,
27387         /**
27388          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27389          */
27390         resizeRegion: undefined,
27391
27392
27393     /**
27394      * Perform a manual resize
27395      * @param {Number} width
27396      * @param {Number} height
27397      */
27398     resizeTo : function(width, height){
27399         this.el.setSize(width, height);
27400         this.updateChildSize();
27401         this.fireEvent("resize", this, width, height, null);
27402     },
27403
27404     // private
27405     startSizing : function(e, handle){
27406         this.fireEvent("beforeresize", this, e);
27407         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27408
27409             if(!this.overlay){
27410                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27411                 this.overlay.unselectable();
27412                 this.overlay.enableDisplayMode("block");
27413                 this.overlay.on("mousemove", this.onMouseMove, this);
27414                 this.overlay.on("mouseup", this.onMouseUp, this);
27415             }
27416             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27417
27418             this.resizing = true;
27419             this.startBox = this.el.getBox();
27420             this.startPoint = e.getXY();
27421             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27422                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27423
27424             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27425             this.overlay.show();
27426
27427             if(this.constrainTo) {
27428                 var ct = Roo.get(this.constrainTo);
27429                 this.resizeRegion = ct.getRegion().adjust(
27430                     ct.getFrameWidth('t'),
27431                     ct.getFrameWidth('l'),
27432                     -ct.getFrameWidth('b'),
27433                     -ct.getFrameWidth('r')
27434                 );
27435             }
27436
27437             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27438             this.proxy.show();
27439             this.proxy.setBox(this.startBox);
27440             if(!this.dynamic){
27441                 this.proxy.setStyle('visibility', 'visible');
27442             }
27443         }
27444     },
27445
27446     // private
27447     onMouseDown : function(handle, e){
27448         if(this.enabled){
27449             e.stopEvent();
27450             this.activeHandle = handle;
27451             this.startSizing(e, handle);
27452         }
27453     },
27454
27455     // private
27456     onMouseUp : function(e){
27457         var size = this.resizeElement();
27458         this.resizing = false;
27459         this.handleOut();
27460         this.overlay.hide();
27461         this.proxy.hide();
27462         this.fireEvent("resize", this, size.width, size.height, e);
27463     },
27464
27465     // private
27466     updateChildSize : function(){
27467         if(this.resizeChild){
27468             var el = this.el;
27469             var child = this.resizeChild;
27470             var adj = this.adjustments;
27471             if(el.dom.offsetWidth){
27472                 var b = el.getSize(true);
27473                 child.setSize(b.width+adj[0], b.height+adj[1]);
27474             }
27475             // Second call here for IE
27476             // The first call enables instant resizing and
27477             // the second call corrects scroll bars if they
27478             // exist
27479             if(Roo.isIE){
27480                 setTimeout(function(){
27481                     if(el.dom.offsetWidth){
27482                         var b = el.getSize(true);
27483                         child.setSize(b.width+adj[0], b.height+adj[1]);
27484                     }
27485                 }, 10);
27486             }
27487         }
27488     },
27489
27490     // private
27491     snap : function(value, inc, min){
27492         if(!inc || !value) return value;
27493         var newValue = value;
27494         var m = value % inc;
27495         if(m > 0){
27496             if(m > (inc/2)){
27497                 newValue = value + (inc-m);
27498             }else{
27499                 newValue = value - m;
27500             }
27501         }
27502         return Math.max(min, newValue);
27503     },
27504
27505     // private
27506     resizeElement : function(){
27507         var box = this.proxy.getBox();
27508         if(this.updateBox){
27509             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27510         }else{
27511             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27512         }
27513         this.updateChildSize();
27514         if(!this.dynamic){
27515             this.proxy.hide();
27516         }
27517         return box;
27518     },
27519
27520     // private
27521     constrain : function(v, diff, m, mx){
27522         if(v - diff < m){
27523             diff = v - m;
27524         }else if(v - diff > mx){
27525             diff = mx - v;
27526         }
27527         return diff;
27528     },
27529
27530     // private
27531     onMouseMove : function(e){
27532         if(this.enabled){
27533             try{// try catch so if something goes wrong the user doesn't get hung
27534
27535             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27536                 return;
27537             }
27538
27539             //var curXY = this.startPoint;
27540             var curSize = this.curSize || this.startBox;
27541             var x = this.startBox.x, y = this.startBox.y;
27542             var ox = x, oy = y;
27543             var w = curSize.width, h = curSize.height;
27544             var ow = w, oh = h;
27545             var mw = this.minWidth, mh = this.minHeight;
27546             var mxw = this.maxWidth, mxh = this.maxHeight;
27547             var wi = this.widthIncrement;
27548             var hi = this.heightIncrement;
27549
27550             var eventXY = e.getXY();
27551             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27552             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27553
27554             var pos = this.activeHandle.position;
27555
27556             switch(pos){
27557                 case "east":
27558                     w += diffX;
27559                     w = Math.min(Math.max(mw, w), mxw);
27560                     break;
27561                 case "south":
27562                     h += diffY;
27563                     h = Math.min(Math.max(mh, h), mxh);
27564                     break;
27565                 case "southeast":
27566                     w += diffX;
27567                     h += diffY;
27568                     w = Math.min(Math.max(mw, w), mxw);
27569                     h = Math.min(Math.max(mh, h), mxh);
27570                     break;
27571                 case "north":
27572                     diffY = this.constrain(h, diffY, mh, mxh);
27573                     y += diffY;
27574                     h -= diffY;
27575                     break;
27576                 case "west":
27577                     diffX = this.constrain(w, diffX, mw, mxw);
27578                     x += diffX;
27579                     w -= diffX;
27580                     break;
27581                 case "northeast":
27582                     w += diffX;
27583                     w = Math.min(Math.max(mw, w), mxw);
27584                     diffY = this.constrain(h, diffY, mh, mxh);
27585                     y += diffY;
27586                     h -= diffY;
27587                     break;
27588                 case "northwest":
27589                     diffX = this.constrain(w, diffX, mw, mxw);
27590                     diffY = this.constrain(h, diffY, mh, mxh);
27591                     y += diffY;
27592                     h -= diffY;
27593                     x += diffX;
27594                     w -= diffX;
27595                     break;
27596                case "southwest":
27597                     diffX = this.constrain(w, diffX, mw, mxw);
27598                     h += diffY;
27599                     h = Math.min(Math.max(mh, h), mxh);
27600                     x += diffX;
27601                     w -= diffX;
27602                     break;
27603             }
27604
27605             var sw = this.snap(w, wi, mw);
27606             var sh = this.snap(h, hi, mh);
27607             if(sw != w || sh != h){
27608                 switch(pos){
27609                     case "northeast":
27610                         y -= sh - h;
27611                     break;
27612                     case "north":
27613                         y -= sh - h;
27614                         break;
27615                     case "southwest":
27616                         x -= sw - w;
27617                     break;
27618                     case "west":
27619                         x -= sw - w;
27620                         break;
27621                     case "northwest":
27622                         x -= sw - w;
27623                         y -= sh - h;
27624                     break;
27625                 }
27626                 w = sw;
27627                 h = sh;
27628             }
27629
27630             if(this.preserveRatio){
27631                 switch(pos){
27632                     case "southeast":
27633                     case "east":
27634                         h = oh * (w/ow);
27635                         h = Math.min(Math.max(mh, h), mxh);
27636                         w = ow * (h/oh);
27637                        break;
27638                     case "south":
27639                         w = ow * (h/oh);
27640                         w = Math.min(Math.max(mw, w), mxw);
27641                         h = oh * (w/ow);
27642                         break;
27643                     case "northeast":
27644                         w = ow * (h/oh);
27645                         w = Math.min(Math.max(mw, w), mxw);
27646                         h = oh * (w/ow);
27647                     break;
27648                     case "north":
27649                         var tw = w;
27650                         w = ow * (h/oh);
27651                         w = Math.min(Math.max(mw, w), mxw);
27652                         h = oh * (w/ow);
27653                         x += (tw - w) / 2;
27654                         break;
27655                     case "southwest":
27656                         h = oh * (w/ow);
27657                         h = Math.min(Math.max(mh, h), mxh);
27658                         var tw = w;
27659                         w = ow * (h/oh);
27660                         x += tw - w;
27661                         break;
27662                     case "west":
27663                         var th = h;
27664                         h = oh * (w/ow);
27665                         h = Math.min(Math.max(mh, h), mxh);
27666                         y += (th - h) / 2;
27667                         var tw = w;
27668                         w = ow * (h/oh);
27669                         x += tw - w;
27670                        break;
27671                     case "northwest":
27672                         var tw = w;
27673                         var th = h;
27674                         h = oh * (w/ow);
27675                         h = Math.min(Math.max(mh, h), mxh);
27676                         w = ow * (h/oh);
27677                         y += th - h;
27678                          x += tw - w;
27679                        break;
27680
27681                 }
27682             }
27683             this.proxy.setBounds(x, y, w, h);
27684             if(this.dynamic){
27685                 this.resizeElement();
27686             }
27687             }catch(e){}
27688         }
27689     },
27690
27691     // private
27692     handleOver : function(){
27693         if(this.enabled){
27694             this.el.addClass("x-resizable-over");
27695         }
27696     },
27697
27698     // private
27699     handleOut : function(){
27700         if(!this.resizing){
27701             this.el.removeClass("x-resizable-over");
27702         }
27703     },
27704
27705     /**
27706      * Returns the element this component is bound to.
27707      * @return {Roo.Element}
27708      */
27709     getEl : function(){
27710         return this.el;
27711     },
27712
27713     /**
27714      * Returns the resizeChild element (or null).
27715      * @return {Roo.Element}
27716      */
27717     getResizeChild : function(){
27718         return this.resizeChild;
27719     },
27720
27721     /**
27722      * Destroys this resizable. If the element was wrapped and
27723      * removeEl is not true then the element remains.
27724      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27725      */
27726     destroy : function(removeEl){
27727         this.proxy.remove();
27728         if(this.overlay){
27729             this.overlay.removeAllListeners();
27730             this.overlay.remove();
27731         }
27732         var ps = Roo.Resizable.positions;
27733         for(var k in ps){
27734             if(typeof ps[k] != "function" && this[ps[k]]){
27735                 var h = this[ps[k]];
27736                 h.el.removeAllListeners();
27737                 h.el.remove();
27738             }
27739         }
27740         if(removeEl){
27741             this.el.update("");
27742             this.el.remove();
27743         }
27744     }
27745 });
27746
27747 // private
27748 // hash to map config positions to true positions
27749 Roo.Resizable.positions = {
27750     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27751 };
27752
27753 // private
27754 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27755     if(!this.tpl){
27756         // only initialize the template if resizable is used
27757         var tpl = Roo.DomHelper.createTemplate(
27758             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27759         );
27760         tpl.compile();
27761         Roo.Resizable.Handle.prototype.tpl = tpl;
27762     }
27763     this.position = pos;
27764     this.rz = rz;
27765     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27766     this.el.unselectable();
27767     if(transparent){
27768         this.el.setOpacity(0);
27769     }
27770     this.el.on("mousedown", this.onMouseDown, this);
27771     if(!disableTrackOver){
27772         this.el.on("mouseover", this.onMouseOver, this);
27773         this.el.on("mouseout", this.onMouseOut, this);
27774     }
27775 };
27776
27777 // private
27778 Roo.Resizable.Handle.prototype = {
27779     afterResize : function(rz){
27780         // do nothing
27781     },
27782     // private
27783     onMouseDown : function(e){
27784         this.rz.onMouseDown(this, e);
27785     },
27786     // private
27787     onMouseOver : function(e){
27788         this.rz.handleOver(this, e);
27789     },
27790     // private
27791     onMouseOut : function(e){
27792         this.rz.handleOut(this, e);
27793     }
27794 };/*
27795  * Based on:
27796  * Ext JS Library 1.1.1
27797  * Copyright(c) 2006-2007, Ext JS, LLC.
27798  *
27799  * Originally Released Under LGPL - original licence link has changed is not relivant.
27800  *
27801  * Fork - LGPL
27802  * <script type="text/javascript">
27803  */
27804
27805 /**
27806  * @class Roo.Editor
27807  * @extends Roo.Component
27808  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27809  * @constructor
27810  * Create a new Editor
27811  * @param {Roo.form.Field} field The Field object (or descendant)
27812  * @param {Object} config The config object
27813  */
27814 Roo.Editor = function(field, config){
27815     Roo.Editor.superclass.constructor.call(this, config);
27816     this.field = field;
27817     this.addEvents({
27818         /**
27819              * @event beforestartedit
27820              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27821              * false from the handler of this event.
27822              * @param {Editor} this
27823              * @param {Roo.Element} boundEl The underlying element bound to this editor
27824              * @param {Mixed} value The field value being set
27825              */
27826         "beforestartedit" : true,
27827         /**
27828              * @event startedit
27829              * Fires when this editor is displayed
27830              * @param {Roo.Element} boundEl The underlying element bound to this editor
27831              * @param {Mixed} value The starting field value
27832              */
27833         "startedit" : true,
27834         /**
27835              * @event beforecomplete
27836              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27837              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27838              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27839              * event will not fire since no edit actually occurred.
27840              * @param {Editor} this
27841              * @param {Mixed} value The current field value
27842              * @param {Mixed} startValue The original field value
27843              */
27844         "beforecomplete" : true,
27845         /**
27846              * @event complete
27847              * Fires after editing is complete and any changed value has been written to the underlying field.
27848              * @param {Editor} this
27849              * @param {Mixed} value The current field value
27850              * @param {Mixed} startValue The original field value
27851              */
27852         "complete" : true,
27853         /**
27854          * @event specialkey
27855          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27856          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27857          * @param {Roo.form.Field} this
27858          * @param {Roo.EventObject} e The event object
27859          */
27860         "specialkey" : true
27861     });
27862 };
27863
27864 Roo.extend(Roo.Editor, Roo.Component, {
27865     /**
27866      * @cfg {Boolean/String} autosize
27867      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27868      * or "height" to adopt the height only (defaults to false)
27869      */
27870     /**
27871      * @cfg {Boolean} revertInvalid
27872      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27873      * validation fails (defaults to true)
27874      */
27875     /**
27876      * @cfg {Boolean} ignoreNoChange
27877      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27878      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27879      * will never be ignored.
27880      */
27881     /**
27882      * @cfg {Boolean} hideEl
27883      * False to keep the bound element visible while the editor is displayed (defaults to true)
27884      */
27885     /**
27886      * @cfg {Mixed} value
27887      * The data value of the underlying field (defaults to "")
27888      */
27889     value : "",
27890     /**
27891      * @cfg {String} alignment
27892      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27893      */
27894     alignment: "c-c?",
27895     /**
27896      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27897      * for bottom-right shadow (defaults to "frame")
27898      */
27899     shadow : "frame",
27900     /**
27901      * @cfg {Boolean} constrain True to constrain the editor to the viewport
27902      */
27903     constrain : false,
27904     /**
27905      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
27906      */
27907     completeOnEnter : false,
27908     /**
27909      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
27910      */
27911     cancelOnEsc : false,
27912     /**
27913      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
27914      */
27915     updateEl : false,
27916
27917     // private
27918     onRender : function(ct, position){
27919         this.el = new Roo.Layer({
27920             shadow: this.shadow,
27921             cls: "x-editor",
27922             parentEl : ct,
27923             shim : this.shim,
27924             shadowOffset:4,
27925             id: this.id,
27926             constrain: this.constrain
27927         });
27928         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
27929         if(this.field.msgTarget != 'title'){
27930             this.field.msgTarget = 'qtip';
27931         }
27932         this.field.render(this.el);
27933         if(Roo.isGecko){
27934             this.field.el.dom.setAttribute('autocomplete', 'off');
27935         }
27936         this.field.on("specialkey", this.onSpecialKey, this);
27937         if(this.swallowKeys){
27938             this.field.el.swallowEvent(['keydown','keypress']);
27939         }
27940         this.field.show();
27941         this.field.on("blur", this.onBlur, this);
27942         if(this.field.grow){
27943             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
27944         }
27945     },
27946
27947     onSpecialKey : function(field, e){
27948         if(this.completeOnEnter && e.getKey() == e.ENTER){
27949             e.stopEvent();
27950             this.completeEdit();
27951         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
27952             this.cancelEdit();
27953         }else{
27954             this.fireEvent('specialkey', field, e);
27955         }
27956     },
27957
27958     /**
27959      * Starts the editing process and shows the editor.
27960      * @param {String/HTMLElement/Element} el The element to edit
27961      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
27962       * to the innerHTML of el.
27963      */
27964     startEdit : function(el, value){
27965         if(this.editing){
27966             this.completeEdit();
27967         }
27968         this.boundEl = Roo.get(el);
27969         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
27970         if(!this.rendered){
27971             this.render(this.parentEl || document.body);
27972         }
27973         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
27974             return;
27975         }
27976         this.startValue = v;
27977         this.field.setValue(v);
27978         if(this.autoSize){
27979             var sz = this.boundEl.getSize();
27980             switch(this.autoSize){
27981                 case "width":
27982                 this.setSize(sz.width,  "");
27983                 break;
27984                 case "height":
27985                 this.setSize("",  sz.height);
27986                 break;
27987                 default:
27988                 this.setSize(sz.width,  sz.height);
27989             }
27990         }
27991         this.el.alignTo(this.boundEl, this.alignment);
27992         this.editing = true;
27993         if(Roo.QuickTips){
27994             Roo.QuickTips.disable();
27995         }
27996         this.show();
27997     },
27998
27999     /**
28000      * Sets the height and width of this editor.
28001      * @param {Number} width The new width
28002      * @param {Number} height The new height
28003      */
28004     setSize : function(w, h){
28005         this.field.setSize(w, h);
28006         if(this.el){
28007             this.el.sync();
28008         }
28009     },
28010
28011     /**
28012      * Realigns the editor to the bound field based on the current alignment config value.
28013      */
28014     realign : function(){
28015         this.el.alignTo(this.boundEl, this.alignment);
28016     },
28017
28018     /**
28019      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28020      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28021      */
28022     completeEdit : function(remainVisible){
28023         if(!this.editing){
28024             return;
28025         }
28026         var v = this.getValue();
28027         if(this.revertInvalid !== false && !this.field.isValid()){
28028             v = this.startValue;
28029             this.cancelEdit(true);
28030         }
28031         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28032             this.editing = false;
28033             this.hide();
28034             return;
28035         }
28036         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28037             this.editing = false;
28038             if(this.updateEl && this.boundEl){
28039                 this.boundEl.update(v);
28040             }
28041             if(remainVisible !== true){
28042                 this.hide();
28043             }
28044             this.fireEvent("complete", this, v, this.startValue);
28045         }
28046     },
28047
28048     // private
28049     onShow : function(){
28050         this.el.show();
28051         if(this.hideEl !== false){
28052             this.boundEl.hide();
28053         }
28054         this.field.show();
28055         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28056             this.fixIEFocus = true;
28057             this.deferredFocus.defer(50, this);
28058         }else{
28059             this.field.focus();
28060         }
28061         this.fireEvent("startedit", this.boundEl, this.startValue);
28062     },
28063
28064     deferredFocus : function(){
28065         if(this.editing){
28066             this.field.focus();
28067         }
28068     },
28069
28070     /**
28071      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28072      * reverted to the original starting value.
28073      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28074      * cancel (defaults to false)
28075      */
28076     cancelEdit : function(remainVisible){
28077         if(this.editing){
28078             this.setValue(this.startValue);
28079             if(remainVisible !== true){
28080                 this.hide();
28081             }
28082         }
28083     },
28084
28085     // private
28086     onBlur : function(){
28087         if(this.allowBlur !== true && this.editing){
28088             this.completeEdit();
28089         }
28090     },
28091
28092     // private
28093     onHide : function(){
28094         if(this.editing){
28095             this.completeEdit();
28096             return;
28097         }
28098         this.field.blur();
28099         if(this.field.collapse){
28100             this.field.collapse();
28101         }
28102         this.el.hide();
28103         if(this.hideEl !== false){
28104             this.boundEl.show();
28105         }
28106         if(Roo.QuickTips){
28107             Roo.QuickTips.enable();
28108         }
28109     },
28110
28111     /**
28112      * Sets the data value of the editor
28113      * @param {Mixed} value Any valid value supported by the underlying field
28114      */
28115     setValue : function(v){
28116         this.field.setValue(v);
28117     },
28118
28119     /**
28120      * Gets the data value of the editor
28121      * @return {Mixed} The data value
28122      */
28123     getValue : function(){
28124         return this.field.getValue();
28125     }
28126 });/*
28127  * Based on:
28128  * Ext JS Library 1.1.1
28129  * Copyright(c) 2006-2007, Ext JS, LLC.
28130  *
28131  * Originally Released Under LGPL - original licence link has changed is not relivant.
28132  *
28133  * Fork - LGPL
28134  * <script type="text/javascript">
28135  */
28136  
28137 /**
28138  * @class Roo.BasicDialog
28139  * @extends Roo.util.Observable
28140  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28141  * <pre><code>
28142 var dlg = new Roo.BasicDialog("my-dlg", {
28143     height: 200,
28144     width: 300,
28145     minHeight: 100,
28146     minWidth: 150,
28147     modal: true,
28148     proxyDrag: true,
28149     shadow: true
28150 });
28151 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28152 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28153 dlg.addButton('Cancel', dlg.hide, dlg);
28154 dlg.show();
28155 </code></pre>
28156   <b>A Dialog should always be a direct child of the body element.</b>
28157  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28158  * @cfg {String} title Default text to display in the title bar (defaults to null)
28159  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28160  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28161  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28162  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28163  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28164  * (defaults to null with no animation)
28165  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28166  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28167  * property for valid values (defaults to 'all')
28168  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28169  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28170  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28171  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28172  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28173  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28174  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28175  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28176  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28177  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28178  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28179  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28180  * draggable = true (defaults to false)
28181  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28182  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28183  * shadow (defaults to false)
28184  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28185  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28186  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28187  * @cfg {Array} buttons Array of buttons
28188  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28189  * @constructor
28190  * Create a new BasicDialog.
28191  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28192  * @param {Object} config Configuration options
28193  */
28194 Roo.BasicDialog = function(el, config){
28195     this.el = Roo.get(el);
28196     var dh = Roo.DomHelper;
28197     if(!this.el && config && config.autoCreate){
28198         if(typeof config.autoCreate == "object"){
28199             if(!config.autoCreate.id){
28200                 config.autoCreate.id = el;
28201             }
28202             this.el = dh.append(document.body,
28203                         config.autoCreate, true);
28204         }else{
28205             this.el = dh.append(document.body,
28206                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28207         }
28208     }
28209     el = this.el;
28210     el.setDisplayed(true);
28211     el.hide = this.hideAction;
28212     this.id = el.id;
28213     el.addClass("x-dlg");
28214
28215     Roo.apply(this, config);
28216
28217     this.proxy = el.createProxy("x-dlg-proxy");
28218     this.proxy.hide = this.hideAction;
28219     this.proxy.setOpacity(.5);
28220     this.proxy.hide();
28221
28222     if(config.width){
28223         el.setWidth(config.width);
28224     }
28225     if(config.height){
28226         el.setHeight(config.height);
28227     }
28228     this.size = el.getSize();
28229     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28230         this.xy = [config.x,config.y];
28231     }else{
28232         this.xy = el.getCenterXY(true);
28233     }
28234     /** The header element @type Roo.Element */
28235     this.header = el.child("> .x-dlg-hd");
28236     /** The body element @type Roo.Element */
28237     this.body = el.child("> .x-dlg-bd");
28238     /** The footer element @type Roo.Element */
28239     this.footer = el.child("> .x-dlg-ft");
28240
28241     if(!this.header){
28242         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28243     }
28244     if(!this.body){
28245         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28246     }
28247
28248     this.header.unselectable();
28249     if(this.title){
28250         this.header.update(this.title);
28251     }
28252     // this element allows the dialog to be focused for keyboard event
28253     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28254     this.focusEl.swallowEvent("click", true);
28255
28256     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28257
28258     // wrap the body and footer for special rendering
28259     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28260     if(this.footer){
28261         this.bwrap.dom.appendChild(this.footer.dom);
28262     }
28263
28264     this.bg = this.el.createChild({
28265         tag: "div", cls:"x-dlg-bg",
28266         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28267     });
28268     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28269
28270
28271     if(this.autoScroll !== false && !this.autoTabs){
28272         this.body.setStyle("overflow", "auto");
28273     }
28274
28275     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28276
28277     if(this.closable !== false){
28278         this.el.addClass("x-dlg-closable");
28279         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28280         this.close.on("click", this.closeClick, this);
28281         this.close.addClassOnOver("x-dlg-close-over");
28282     }
28283     if(this.collapsible !== false){
28284         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28285         this.collapseBtn.on("click", this.collapseClick, this);
28286         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28287         this.header.on("dblclick", this.collapseClick, this);
28288     }
28289     if(this.resizable !== false){
28290         this.el.addClass("x-dlg-resizable");
28291         this.resizer = new Roo.Resizable(el, {
28292             minWidth: this.minWidth || 80,
28293             minHeight:this.minHeight || 80,
28294             handles: this.resizeHandles || "all",
28295             pinned: true
28296         });
28297         this.resizer.on("beforeresize", this.beforeResize, this);
28298         this.resizer.on("resize", this.onResize, this);
28299     }
28300     if(this.draggable !== false){
28301         el.addClass("x-dlg-draggable");
28302         if (!this.proxyDrag) {
28303             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28304         }
28305         else {
28306             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28307         }
28308         dd.setHandleElId(this.header.id);
28309         dd.endDrag = this.endMove.createDelegate(this);
28310         dd.startDrag = this.startMove.createDelegate(this);
28311         dd.onDrag = this.onDrag.createDelegate(this);
28312         dd.scroll = false;
28313         this.dd = dd;
28314     }
28315     if(this.modal){
28316         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28317         this.mask.enableDisplayMode("block");
28318         this.mask.hide();
28319         this.el.addClass("x-dlg-modal");
28320     }
28321     if(this.shadow){
28322         this.shadow = new Roo.Shadow({
28323             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28324             offset : this.shadowOffset
28325         });
28326     }else{
28327         this.shadowOffset = 0;
28328     }
28329     if(Roo.useShims && this.shim !== false){
28330         this.shim = this.el.createShim();
28331         this.shim.hide = this.hideAction;
28332         this.shim.hide();
28333     }else{
28334         this.shim = false;
28335     }
28336     if(this.autoTabs){
28337         this.initTabs();
28338     }
28339     if (this.buttons) { 
28340         var bts= this.buttons;
28341         this.buttons = [];
28342         Roo.each(bts, function(b) {
28343             this.addButton(b);
28344         }, this);
28345     }
28346     
28347     
28348     this.addEvents({
28349         /**
28350          * @event keydown
28351          * Fires when a key is pressed
28352          * @param {Roo.BasicDialog} this
28353          * @param {Roo.EventObject} e
28354          */
28355         "keydown" : true,
28356         /**
28357          * @event move
28358          * Fires when this dialog is moved by the user.
28359          * @param {Roo.BasicDialog} this
28360          * @param {Number} x The new page X
28361          * @param {Number} y The new page Y
28362          */
28363         "move" : true,
28364         /**
28365          * @event resize
28366          * Fires when this dialog is resized by the user.
28367          * @param {Roo.BasicDialog} this
28368          * @param {Number} width The new width
28369          * @param {Number} height The new height
28370          */
28371         "resize" : true,
28372         /**
28373          * @event beforehide
28374          * Fires before this dialog is hidden.
28375          * @param {Roo.BasicDialog} this
28376          */
28377         "beforehide" : true,
28378         /**
28379          * @event hide
28380          * Fires when this dialog is hidden.
28381          * @param {Roo.BasicDialog} this
28382          */
28383         "hide" : true,
28384         /**
28385          * @event beforeshow
28386          * Fires before this dialog is shown.
28387          * @param {Roo.BasicDialog} this
28388          */
28389         "beforeshow" : true,
28390         /**
28391          * @event show
28392          * Fires when this dialog is shown.
28393          * @param {Roo.BasicDialog} this
28394          */
28395         "show" : true
28396     });
28397     el.on("keydown", this.onKeyDown, this);
28398     el.on("mousedown", this.toFront, this);
28399     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28400     this.el.hide();
28401     Roo.DialogManager.register(this);
28402     Roo.BasicDialog.superclass.constructor.call(this);
28403 };
28404
28405 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28406     shadowOffset: Roo.isIE ? 6 : 5,
28407     minHeight: 80,
28408     minWidth: 200,
28409     minButtonWidth: 75,
28410     defaultButton: null,
28411     buttonAlign: "right",
28412     tabTag: 'div',
28413     firstShow: true,
28414
28415     /**
28416      * Sets the dialog title text
28417      * @param {String} text The title text to display
28418      * @return {Roo.BasicDialog} this
28419      */
28420     setTitle : function(text){
28421         this.header.update(text);
28422         return this;
28423     },
28424
28425     // private
28426     closeClick : function(){
28427         this.hide();
28428     },
28429
28430     // private
28431     collapseClick : function(){
28432         this[this.collapsed ? "expand" : "collapse"]();
28433     },
28434
28435     /**
28436      * Collapses the dialog to its minimized state (only the title bar is visible).
28437      * Equivalent to the user clicking the collapse dialog button.
28438      */
28439     collapse : function(){
28440         if(!this.collapsed){
28441             this.collapsed = true;
28442             this.el.addClass("x-dlg-collapsed");
28443             this.restoreHeight = this.el.getHeight();
28444             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28445         }
28446     },
28447
28448     /**
28449      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28450      * clicking the expand dialog button.
28451      */
28452     expand : function(){
28453         if(this.collapsed){
28454             this.collapsed = false;
28455             this.el.removeClass("x-dlg-collapsed");
28456             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28457         }
28458     },
28459
28460     /**
28461      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28462      * @return {Roo.TabPanel} The tabs component
28463      */
28464     initTabs : function(){
28465         var tabs = this.getTabs();
28466         while(tabs.getTab(0)){
28467             tabs.removeTab(0);
28468         }
28469         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28470             var dom = el.dom;
28471             tabs.addTab(Roo.id(dom), dom.title);
28472             dom.title = "";
28473         });
28474         tabs.activate(0);
28475         return tabs;
28476     },
28477
28478     // private
28479     beforeResize : function(){
28480         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28481     },
28482
28483     // private
28484     onResize : function(){
28485         this.refreshSize();
28486         this.syncBodyHeight();
28487         this.adjustAssets();
28488         this.focus();
28489         this.fireEvent("resize", this, this.size.width, this.size.height);
28490     },
28491
28492     // private
28493     onKeyDown : function(e){
28494         if(this.isVisible()){
28495             this.fireEvent("keydown", this, e);
28496         }
28497     },
28498
28499     /**
28500      * Resizes the dialog.
28501      * @param {Number} width
28502      * @param {Number} height
28503      * @return {Roo.BasicDialog} this
28504      */
28505     resizeTo : function(width, height){
28506         this.el.setSize(width, height);
28507         this.size = {width: width, height: height};
28508         this.syncBodyHeight();
28509         if(this.fixedcenter){
28510             this.center();
28511         }
28512         if(this.isVisible()){
28513             this.constrainXY();
28514             this.adjustAssets();
28515         }
28516         this.fireEvent("resize", this, width, height);
28517         return this;
28518     },
28519
28520
28521     /**
28522      * Resizes the dialog to fit the specified content size.
28523      * @param {Number} width
28524      * @param {Number} height
28525      * @return {Roo.BasicDialog} this
28526      */
28527     setContentSize : function(w, h){
28528         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28529         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28530         //if(!this.el.isBorderBox()){
28531             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28532             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28533         //}
28534         if(this.tabs){
28535             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28536             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28537         }
28538         this.resizeTo(w, h);
28539         return this;
28540     },
28541
28542     /**
28543      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28544      * executed in response to a particular key being pressed while the dialog is active.
28545      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28546      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28547      * @param {Function} fn The function to call
28548      * @param {Object} scope (optional) The scope of the function
28549      * @return {Roo.BasicDialog} this
28550      */
28551     addKeyListener : function(key, fn, scope){
28552         var keyCode, shift, ctrl, alt;
28553         if(typeof key == "object" && !(key instanceof Array)){
28554             keyCode = key["key"];
28555             shift = key["shift"];
28556             ctrl = key["ctrl"];
28557             alt = key["alt"];
28558         }else{
28559             keyCode = key;
28560         }
28561         var handler = function(dlg, e){
28562             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28563                 var k = e.getKey();
28564                 if(keyCode instanceof Array){
28565                     for(var i = 0, len = keyCode.length; i < len; i++){
28566                         if(keyCode[i] == k){
28567                           fn.call(scope || window, dlg, k, e);
28568                           return;
28569                         }
28570                     }
28571                 }else{
28572                     if(k == keyCode){
28573                         fn.call(scope || window, dlg, k, e);
28574                     }
28575                 }
28576             }
28577         };
28578         this.on("keydown", handler);
28579         return this;
28580     },
28581
28582     /**
28583      * Returns the TabPanel component (creates it if it doesn't exist).
28584      * Note: If you wish to simply check for the existence of tabs without creating them,
28585      * check for a null 'tabs' property.
28586      * @return {Roo.TabPanel} The tabs component
28587      */
28588     getTabs : function(){
28589         if(!this.tabs){
28590             this.el.addClass("x-dlg-auto-tabs");
28591             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28592             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28593         }
28594         return this.tabs;
28595     },
28596
28597     /**
28598      * Adds a button to the footer section of the dialog.
28599      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28600      * object or a valid Roo.DomHelper element config
28601      * @param {Function} handler The function called when the button is clicked
28602      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28603      * @return {Roo.Button} The new button
28604      */
28605     addButton : function(config, handler, scope){
28606         var dh = Roo.DomHelper;
28607         if(!this.footer){
28608             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28609         }
28610         if(!this.btnContainer){
28611             var tb = this.footer.createChild({
28612
28613                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28614                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28615             }, null, true);
28616             this.btnContainer = tb.firstChild.firstChild.firstChild;
28617         }
28618         var bconfig = {
28619             handler: handler,
28620             scope: scope,
28621             minWidth: this.minButtonWidth,
28622             hideParent:true
28623         };
28624         if(typeof config == "string"){
28625             bconfig.text = config;
28626         }else{
28627             if(config.tag){
28628                 bconfig.dhconfig = config;
28629             }else{
28630                 Roo.apply(bconfig, config);
28631             }
28632         }
28633         var fc = false;
28634         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28635             bconfig.position = Math.max(0, bconfig.position);
28636             fc = this.btnContainer.childNodes[bconfig.position];
28637         }
28638          
28639         var btn = new Roo.Button(
28640             fc ? 
28641                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28642                 : this.btnContainer.appendChild(document.createElement("td")),
28643             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28644             bconfig
28645         );
28646         this.syncBodyHeight();
28647         if(!this.buttons){
28648             /**
28649              * Array of all the buttons that have been added to this dialog via addButton
28650              * @type Array
28651              */
28652             this.buttons = [];
28653         }
28654         this.buttons.push(btn);
28655         return btn;
28656     },
28657
28658     /**
28659      * Sets the default button to be focused when the dialog is displayed.
28660      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28661      * @return {Roo.BasicDialog} this
28662      */
28663     setDefaultButton : function(btn){
28664         this.defaultButton = btn;
28665         return this;
28666     },
28667
28668     // private
28669     getHeaderFooterHeight : function(safe){
28670         var height = 0;
28671         if(this.header){
28672            height += this.header.getHeight();
28673         }
28674         if(this.footer){
28675            var fm = this.footer.getMargins();
28676             height += (this.footer.getHeight()+fm.top+fm.bottom);
28677         }
28678         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28679         height += this.centerBg.getPadding("tb");
28680         return height;
28681     },
28682
28683     // private
28684     syncBodyHeight : function(){
28685         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28686         var height = this.size.height - this.getHeaderFooterHeight(false);
28687         bd.setHeight(height-bd.getMargins("tb"));
28688         var hh = this.header.getHeight();
28689         var h = this.size.height-hh;
28690         cb.setHeight(h);
28691         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28692         bw.setHeight(h-cb.getPadding("tb"));
28693         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28694         bd.setWidth(bw.getWidth(true));
28695         if(this.tabs){
28696             this.tabs.syncHeight();
28697             if(Roo.isIE){
28698                 this.tabs.el.repaint();
28699             }
28700         }
28701     },
28702
28703     /**
28704      * Restores the previous state of the dialog if Roo.state is configured.
28705      * @return {Roo.BasicDialog} this
28706      */
28707     restoreState : function(){
28708         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28709         if(box && box.width){
28710             this.xy = [box.x, box.y];
28711             this.resizeTo(box.width, box.height);
28712         }
28713         return this;
28714     },
28715
28716     // private
28717     beforeShow : function(){
28718         this.expand();
28719         if(this.fixedcenter){
28720             this.xy = this.el.getCenterXY(true);
28721         }
28722         if(this.modal){
28723             Roo.get(document.body).addClass("x-body-masked");
28724             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28725             this.mask.show();
28726         }
28727         this.constrainXY();
28728     },
28729
28730     // private
28731     animShow : function(){
28732         var b = Roo.get(this.animateTarget, true).getBox();
28733         this.proxy.setSize(b.width, b.height);
28734         this.proxy.setLocation(b.x, b.y);
28735         this.proxy.show();
28736         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28737                     true, .35, this.showEl.createDelegate(this));
28738     },
28739
28740     /**
28741      * Shows the dialog.
28742      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28743      * @return {Roo.BasicDialog} this
28744      */
28745     show : function(animateTarget){
28746         if (this.fireEvent("beforeshow", this) === false){
28747             return;
28748         }
28749         if(this.syncHeightBeforeShow){
28750             this.syncBodyHeight();
28751         }else if(this.firstShow){
28752             this.firstShow = false;
28753             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28754         }
28755         this.animateTarget = animateTarget || this.animateTarget;
28756         if(!this.el.isVisible()){
28757             this.beforeShow();
28758             if(this.animateTarget){
28759                 this.animShow();
28760             }else{
28761                 this.showEl();
28762             }
28763         }
28764         return this;
28765     },
28766
28767     // private
28768     showEl : function(){
28769         this.proxy.hide();
28770         this.el.setXY(this.xy);
28771         this.el.show();
28772         this.adjustAssets(true);
28773         this.toFront();
28774         this.focus();
28775         // IE peekaboo bug - fix found by Dave Fenwick
28776         if(Roo.isIE){
28777             this.el.repaint();
28778         }
28779         this.fireEvent("show", this);
28780     },
28781
28782     /**
28783      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28784      * dialog itself will receive focus.
28785      */
28786     focus : function(){
28787         if(this.defaultButton){
28788             this.defaultButton.focus();
28789         }else{
28790             this.focusEl.focus();
28791         }
28792     },
28793
28794     // private
28795     constrainXY : function(){
28796         if(this.constraintoviewport !== false){
28797             if(!this.viewSize){
28798                 if(this.container){
28799                     var s = this.container.getSize();
28800                     this.viewSize = [s.width, s.height];
28801                 }else{
28802                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28803                 }
28804             }
28805             var s = Roo.get(this.container||document).getScroll();
28806
28807             var x = this.xy[0], y = this.xy[1];
28808             var w = this.size.width, h = this.size.height;
28809             var vw = this.viewSize[0], vh = this.viewSize[1];
28810             // only move it if it needs it
28811             var moved = false;
28812             // first validate right/bottom
28813             if(x + w > vw+s.left){
28814                 x = vw - w;
28815                 moved = true;
28816             }
28817             if(y + h > vh+s.top){
28818                 y = vh - h;
28819                 moved = true;
28820             }
28821             // then make sure top/left isn't negative
28822             if(x < s.left){
28823                 x = s.left;
28824                 moved = true;
28825             }
28826             if(y < s.top){
28827                 y = s.top;
28828                 moved = true;
28829             }
28830             if(moved){
28831                 // cache xy
28832                 this.xy = [x, y];
28833                 if(this.isVisible()){
28834                     this.el.setLocation(x, y);
28835                     this.adjustAssets();
28836                 }
28837             }
28838         }
28839     },
28840
28841     // private
28842     onDrag : function(){
28843         if(!this.proxyDrag){
28844             this.xy = this.el.getXY();
28845             this.adjustAssets();
28846         }
28847     },
28848
28849     // private
28850     adjustAssets : function(doShow){
28851         var x = this.xy[0], y = this.xy[1];
28852         var w = this.size.width, h = this.size.height;
28853         if(doShow === true){
28854             if(this.shadow){
28855                 this.shadow.show(this.el);
28856             }
28857             if(this.shim){
28858                 this.shim.show();
28859             }
28860         }
28861         if(this.shadow && this.shadow.isVisible()){
28862             this.shadow.show(this.el);
28863         }
28864         if(this.shim && this.shim.isVisible()){
28865             this.shim.setBounds(x, y, w, h);
28866         }
28867     },
28868
28869     // private
28870     adjustViewport : function(w, h){
28871         if(!w || !h){
28872             w = Roo.lib.Dom.getViewWidth();
28873             h = Roo.lib.Dom.getViewHeight();
28874         }
28875         // cache the size
28876         this.viewSize = [w, h];
28877         if(this.modal && this.mask.isVisible()){
28878             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28879             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28880         }
28881         if(this.isVisible()){
28882             this.constrainXY();
28883         }
28884     },
28885
28886     /**
28887      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28888      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28889      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28890      */
28891     destroy : function(removeEl){
28892         if(this.isVisible()){
28893             this.animateTarget = null;
28894             this.hide();
28895         }
28896         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28897         if(this.tabs){
28898             this.tabs.destroy(removeEl);
28899         }
28900         Roo.destroy(
28901              this.shim,
28902              this.proxy,
28903              this.resizer,
28904              this.close,
28905              this.mask
28906         );
28907         if(this.dd){
28908             this.dd.unreg();
28909         }
28910         if(this.buttons){
28911            for(var i = 0, len = this.buttons.length; i < len; i++){
28912                this.buttons[i].destroy();
28913            }
28914         }
28915         this.el.removeAllListeners();
28916         if(removeEl === true){
28917             this.el.update("");
28918             this.el.remove();
28919         }
28920         Roo.DialogManager.unregister(this);
28921     },
28922
28923     // private
28924     startMove : function(){
28925         if(this.proxyDrag){
28926             this.proxy.show();
28927         }
28928         if(this.constraintoviewport !== false){
28929             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
28930         }
28931     },
28932
28933     // private
28934     endMove : function(){
28935         if(!this.proxyDrag){
28936             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
28937         }else{
28938             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
28939             this.proxy.hide();
28940         }
28941         this.refreshSize();
28942         this.adjustAssets();
28943         this.focus();
28944         this.fireEvent("move", this, this.xy[0], this.xy[1]);
28945     },
28946
28947     /**
28948      * Brings this dialog to the front of any other visible dialogs
28949      * @return {Roo.BasicDialog} this
28950      */
28951     toFront : function(){
28952         Roo.DialogManager.bringToFront(this);
28953         return this;
28954     },
28955
28956     /**
28957      * Sends this dialog to the back (under) of any other visible dialogs
28958      * @return {Roo.BasicDialog} this
28959      */
28960     toBack : function(){
28961         Roo.DialogManager.sendToBack(this);
28962         return this;
28963     },
28964
28965     /**
28966      * Centers this dialog in the viewport
28967      * @return {Roo.BasicDialog} this
28968      */
28969     center : function(){
28970         var xy = this.el.getCenterXY(true);
28971         this.moveTo(xy[0], xy[1]);
28972         return this;
28973     },
28974
28975     /**
28976      * Moves the dialog's top-left corner to the specified point
28977      * @param {Number} x
28978      * @param {Number} y
28979      * @return {Roo.BasicDialog} this
28980      */
28981     moveTo : function(x, y){
28982         this.xy = [x,y];
28983         if(this.isVisible()){
28984             this.el.setXY(this.xy);
28985             this.adjustAssets();
28986         }
28987         return this;
28988     },
28989
28990     /**
28991      * Aligns the dialog to the specified element
28992      * @param {String/HTMLElement/Roo.Element} element The element to align to.
28993      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
28994      * @param {Array} offsets (optional) Offset the positioning by [x, y]
28995      * @return {Roo.BasicDialog} this
28996      */
28997     alignTo : function(element, position, offsets){
28998         this.xy = this.el.getAlignToXY(element, position, offsets);
28999         if(this.isVisible()){
29000             this.el.setXY(this.xy);
29001             this.adjustAssets();
29002         }
29003         return this;
29004     },
29005
29006     /**
29007      * Anchors an element to another element and realigns it when the window is resized.
29008      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29009      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29010      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29011      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29012      * is a number, it is used as the buffer delay (defaults to 50ms).
29013      * @return {Roo.BasicDialog} this
29014      */
29015     anchorTo : function(el, alignment, offsets, monitorScroll){
29016         var action = function(){
29017             this.alignTo(el, alignment, offsets);
29018         };
29019         Roo.EventManager.onWindowResize(action, this);
29020         var tm = typeof monitorScroll;
29021         if(tm != 'undefined'){
29022             Roo.EventManager.on(window, 'scroll', action, this,
29023                 {buffer: tm == 'number' ? monitorScroll : 50});
29024         }
29025         action.call(this);
29026         return this;
29027     },
29028
29029     /**
29030      * Returns true if the dialog is visible
29031      * @return {Boolean}
29032      */
29033     isVisible : function(){
29034         return this.el.isVisible();
29035     },
29036
29037     // private
29038     animHide : function(callback){
29039         var b = Roo.get(this.animateTarget).getBox();
29040         this.proxy.show();
29041         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29042         this.el.hide();
29043         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29044                     this.hideEl.createDelegate(this, [callback]));
29045     },
29046
29047     /**
29048      * Hides the dialog.
29049      * @param {Function} callback (optional) Function to call when the dialog is hidden
29050      * @return {Roo.BasicDialog} this
29051      */
29052     hide : function(callback){
29053         if (this.fireEvent("beforehide", this) === false){
29054             return;
29055         }
29056         if(this.shadow){
29057             this.shadow.hide();
29058         }
29059         if(this.shim) {
29060           this.shim.hide();
29061         }
29062         if(this.animateTarget){
29063            this.animHide(callback);
29064         }else{
29065             this.el.hide();
29066             this.hideEl(callback);
29067         }
29068         return this;
29069     },
29070
29071     // private
29072     hideEl : function(callback){
29073         this.proxy.hide();
29074         if(this.modal){
29075             this.mask.hide();
29076             Roo.get(document.body).removeClass("x-body-masked");
29077         }
29078         this.fireEvent("hide", this);
29079         if(typeof callback == "function"){
29080             callback();
29081         }
29082     },
29083
29084     // private
29085     hideAction : function(){
29086         this.setLeft("-10000px");
29087         this.setTop("-10000px");
29088         this.setStyle("visibility", "hidden");
29089     },
29090
29091     // private
29092     refreshSize : function(){
29093         this.size = this.el.getSize();
29094         this.xy = this.el.getXY();
29095         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29096     },
29097
29098     // private
29099     // z-index is managed by the DialogManager and may be overwritten at any time
29100     setZIndex : function(index){
29101         if(this.modal){
29102             this.mask.setStyle("z-index", index);
29103         }
29104         if(this.shim){
29105             this.shim.setStyle("z-index", ++index);
29106         }
29107         if(this.shadow){
29108             this.shadow.setZIndex(++index);
29109         }
29110         this.el.setStyle("z-index", ++index);
29111         if(this.proxy){
29112             this.proxy.setStyle("z-index", ++index);
29113         }
29114         if(this.resizer){
29115             this.resizer.proxy.setStyle("z-index", ++index);
29116         }
29117
29118         this.lastZIndex = index;
29119     },
29120
29121     /**
29122      * Returns the element for this dialog
29123      * @return {Roo.Element} The underlying dialog Element
29124      */
29125     getEl : function(){
29126         return this.el;
29127     }
29128 });
29129
29130 /**
29131  * @class Roo.DialogManager
29132  * Provides global access to BasicDialogs that have been created and
29133  * support for z-indexing (layering) multiple open dialogs.
29134  */
29135 Roo.DialogManager = function(){
29136     var list = {};
29137     var accessList = [];
29138     var front = null;
29139
29140     // private
29141     var sortDialogs = function(d1, d2){
29142         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29143     };
29144
29145     // private
29146     var orderDialogs = function(){
29147         accessList.sort(sortDialogs);
29148         var seed = Roo.DialogManager.zseed;
29149         for(var i = 0, len = accessList.length; i < len; i++){
29150             var dlg = accessList[i];
29151             if(dlg){
29152                 dlg.setZIndex(seed + (i*10));
29153             }
29154         }
29155     };
29156
29157     return {
29158         /**
29159          * The starting z-index for BasicDialogs (defaults to 9000)
29160          * @type Number The z-index value
29161          */
29162         zseed : 9000,
29163
29164         // private
29165         register : function(dlg){
29166             list[dlg.id] = dlg;
29167             accessList.push(dlg);
29168         },
29169
29170         // private
29171         unregister : function(dlg){
29172             delete list[dlg.id];
29173             var i=0;
29174             var len=0;
29175             if(!accessList.indexOf){
29176                 for(  i = 0, len = accessList.length; i < len; i++){
29177                     if(accessList[i] == dlg){
29178                         accessList.splice(i, 1);
29179                         return;
29180                     }
29181                 }
29182             }else{
29183                  i = accessList.indexOf(dlg);
29184                 if(i != -1){
29185                     accessList.splice(i, 1);
29186                 }
29187             }
29188         },
29189
29190         /**
29191          * Gets a registered dialog by id
29192          * @param {String/Object} id The id of the dialog or a dialog
29193          * @return {Roo.BasicDialog} this
29194          */
29195         get : function(id){
29196             return typeof id == "object" ? id : list[id];
29197         },
29198
29199         /**
29200          * Brings the specified dialog to the front
29201          * @param {String/Object} dlg The id of the dialog or a dialog
29202          * @return {Roo.BasicDialog} this
29203          */
29204         bringToFront : function(dlg){
29205             dlg = this.get(dlg);
29206             if(dlg != front){
29207                 front = dlg;
29208                 dlg._lastAccess = new Date().getTime();
29209                 orderDialogs();
29210             }
29211             return dlg;
29212         },
29213
29214         /**
29215          * Sends the specified dialog to the back
29216          * @param {String/Object} dlg The id of the dialog or a dialog
29217          * @return {Roo.BasicDialog} this
29218          */
29219         sendToBack : function(dlg){
29220             dlg = this.get(dlg);
29221             dlg._lastAccess = -(new Date().getTime());
29222             orderDialogs();
29223             return dlg;
29224         },
29225
29226         /**
29227          * Hides all dialogs
29228          */
29229         hideAll : function(){
29230             for(var id in list){
29231                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29232                     list[id].hide();
29233                 }
29234             }
29235         }
29236     };
29237 }();
29238
29239 /**
29240  * @class Roo.LayoutDialog
29241  * @extends Roo.BasicDialog
29242  * Dialog which provides adjustments for working with a layout in a Dialog.
29243  * Add your necessary layout config options to the dialog's config.<br>
29244  * Example usage (including a nested layout):
29245  * <pre><code>
29246 if(!dialog){
29247     dialog = new Roo.LayoutDialog("download-dlg", {
29248         modal: true,
29249         width:600,
29250         height:450,
29251         shadow:true,
29252         minWidth:500,
29253         minHeight:350,
29254         autoTabs:true,
29255         proxyDrag:true,
29256         // layout config merges with the dialog config
29257         center:{
29258             tabPosition: "top",
29259             alwaysShowTabs: true
29260         }
29261     });
29262     dialog.addKeyListener(27, dialog.hide, dialog);
29263     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29264     dialog.addButton("Build It!", this.getDownload, this);
29265
29266     // we can even add nested layouts
29267     var innerLayout = new Roo.BorderLayout("dl-inner", {
29268         east: {
29269             initialSize: 200,
29270             autoScroll:true,
29271             split:true
29272         },
29273         center: {
29274             autoScroll:true
29275         }
29276     });
29277     innerLayout.beginUpdate();
29278     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29279     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29280     innerLayout.endUpdate(true);
29281
29282     var layout = dialog.getLayout();
29283     layout.beginUpdate();
29284     layout.add("center", new Roo.ContentPanel("standard-panel",
29285                         {title: "Download the Source", fitToFrame:true}));
29286     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29287                {title: "Build your own roo.js"}));
29288     layout.getRegion("center").showPanel(sp);
29289     layout.endUpdate();
29290 }
29291 </code></pre>
29292     * @constructor
29293     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29294     * @param {Object} config configuration options
29295   */
29296 Roo.LayoutDialog = function(el, cfg){
29297     
29298     var config=  cfg;
29299     if (typeof(cfg) == 'undefined') {
29300         config = Roo.apply({}, el);
29301         el = Roo.get( document.documentElement || document.body).createChild();
29302         //config.autoCreate = true;
29303     }
29304     
29305     
29306     config.autoTabs = false;
29307     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29308     this.body.setStyle({overflow:"hidden", position:"relative"});
29309     this.layout = new Roo.BorderLayout(this.body.dom, config);
29310     this.layout.monitorWindowResize = false;
29311     this.el.addClass("x-dlg-auto-layout");
29312     // fix case when center region overwrites center function
29313     this.center = Roo.BasicDialog.prototype.center;
29314     this.on("show", this.layout.layout, this.layout, true);
29315     if (config.items) {
29316         var xitems = config.items;
29317         delete config.items;
29318         Roo.each(xitems, this.addxtype, this);
29319     }
29320     
29321     
29322 };
29323 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29324     /**
29325      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29326      * @deprecated
29327      */
29328     endUpdate : function(){
29329         this.layout.endUpdate();
29330     },
29331
29332     /**
29333      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29334      *  @deprecated
29335      */
29336     beginUpdate : function(){
29337         this.layout.beginUpdate();
29338     },
29339
29340     /**
29341      * Get the BorderLayout for this dialog
29342      * @return {Roo.BorderLayout}
29343      */
29344     getLayout : function(){
29345         return this.layout;
29346     },
29347
29348     showEl : function(){
29349         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29350         if(Roo.isIE7){
29351             this.layout.layout();
29352         }
29353     },
29354
29355     // private
29356     // Use the syncHeightBeforeShow config option to control this automatically
29357     syncBodyHeight : function(){
29358         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29359         if(this.layout){this.layout.layout();}
29360     },
29361     
29362       /**
29363      * Add an xtype element (actually adds to the layout.)
29364      * @return {Object} xdata xtype object data.
29365      */
29366     
29367     addxtype : function(c) {
29368         return this.layout.addxtype(c);
29369     }
29370 });/*
29371  * Based on:
29372  * Ext JS Library 1.1.1
29373  * Copyright(c) 2006-2007, Ext JS, LLC.
29374  *
29375  * Originally Released Under LGPL - original licence link has changed is not relivant.
29376  *
29377  * Fork - LGPL
29378  * <script type="text/javascript">
29379  */
29380  
29381 /**
29382  * @class Roo.MessageBox
29383  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29384  * Example usage:
29385  *<pre><code>
29386 // Basic alert:
29387 Roo.Msg.alert('Status', 'Changes saved successfully.');
29388
29389 // Prompt for user data:
29390 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29391     if (btn == 'ok'){
29392         // process text value...
29393     }
29394 });
29395
29396 // Show a dialog using config options:
29397 Roo.Msg.show({
29398    title:'Save Changes?',
29399    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29400    buttons: Roo.Msg.YESNOCANCEL,
29401    fn: processResult,
29402    animEl: 'elId'
29403 });
29404 </code></pre>
29405  * @singleton
29406  */
29407 Roo.MessageBox = function(){
29408     var dlg, opt, mask, waitTimer;
29409     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29410     var buttons, activeTextEl, bwidth;
29411
29412     // private
29413     var handleButton = function(button){
29414         dlg.hide();
29415         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29416     };
29417
29418     // private
29419     var handleHide = function(){
29420         if(opt && opt.cls){
29421             dlg.el.removeClass(opt.cls);
29422         }
29423         if(waitTimer){
29424             Roo.TaskMgr.stop(waitTimer);
29425             waitTimer = null;
29426         }
29427     };
29428
29429     // private
29430     var updateButtons = function(b){
29431         var width = 0;
29432         if(!b){
29433             buttons["ok"].hide();
29434             buttons["cancel"].hide();
29435             buttons["yes"].hide();
29436             buttons["no"].hide();
29437             dlg.footer.dom.style.display = 'none';
29438             return width;
29439         }
29440         dlg.footer.dom.style.display = '';
29441         for(var k in buttons){
29442             if(typeof buttons[k] != "function"){
29443                 if(b[k]){
29444                     buttons[k].show();
29445                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29446                     width += buttons[k].el.getWidth()+15;
29447                 }else{
29448                     buttons[k].hide();
29449                 }
29450             }
29451         }
29452         return width;
29453     };
29454
29455     // private
29456     var handleEsc = function(d, k, e){
29457         if(opt && opt.closable !== false){
29458             dlg.hide();
29459         }
29460         if(e){
29461             e.stopEvent();
29462         }
29463     };
29464
29465     return {
29466         /**
29467          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29468          * @return {Roo.BasicDialog} The BasicDialog element
29469          */
29470         getDialog : function(){
29471            if(!dlg){
29472                 dlg = new Roo.BasicDialog("x-msg-box", {
29473                     autoCreate : true,
29474                     shadow: true,
29475                     draggable: true,
29476                     resizable:false,
29477                     constraintoviewport:false,
29478                     fixedcenter:true,
29479                     collapsible : false,
29480                     shim:true,
29481                     modal: true,
29482                     width:400, height:100,
29483                     buttonAlign:"center",
29484                     closeClick : function(){
29485                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29486                             handleButton("no");
29487                         }else{
29488                             handleButton("cancel");
29489                         }
29490                     }
29491                 });
29492                 dlg.on("hide", handleHide);
29493                 mask = dlg.mask;
29494                 dlg.addKeyListener(27, handleEsc);
29495                 buttons = {};
29496                 var bt = this.buttonText;
29497                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29498                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29499                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29500                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29501                 bodyEl = dlg.body.createChild({
29502
29503                     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>'
29504                 });
29505                 msgEl = bodyEl.dom.firstChild;
29506                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29507                 textboxEl.enableDisplayMode();
29508                 textboxEl.addKeyListener([10,13], function(){
29509                     if(dlg.isVisible() && opt && opt.buttons){
29510                         if(opt.buttons.ok){
29511                             handleButton("ok");
29512                         }else if(opt.buttons.yes){
29513                             handleButton("yes");
29514                         }
29515                     }
29516                 });
29517                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29518                 textareaEl.enableDisplayMode();
29519                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29520                 progressEl.enableDisplayMode();
29521                 var pf = progressEl.dom.firstChild;
29522                 if (pf) {
29523                     pp = Roo.get(pf.firstChild);
29524                     pp.setHeight(pf.offsetHeight);
29525                 }
29526                 
29527             }
29528             return dlg;
29529         },
29530
29531         /**
29532          * Updates the message box body text
29533          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29534          * the XHTML-compliant non-breaking space character '&amp;#160;')
29535          * @return {Roo.MessageBox} This message box
29536          */
29537         updateText : function(text){
29538             if(!dlg.isVisible() && !opt.width){
29539                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29540             }
29541             msgEl.innerHTML = text || '&#160;';
29542             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29543                         Math.max(opt.minWidth || this.minWidth, bwidth));
29544             if(opt.prompt){
29545                 activeTextEl.setWidth(w);
29546             }
29547             if(dlg.isVisible()){
29548                 dlg.fixedcenter = false;
29549             }
29550             dlg.setContentSize(w, bodyEl.getHeight());
29551             if(dlg.isVisible()){
29552                 dlg.fixedcenter = true;
29553             }
29554             return this;
29555         },
29556
29557         /**
29558          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29559          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29560          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29561          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29562          * @return {Roo.MessageBox} This message box
29563          */
29564         updateProgress : function(value, text){
29565             if(text){
29566                 this.updateText(text);
29567             }
29568             if (pp) { // weird bug on my firefox - for some reason this is not defined
29569                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29570             }
29571             return this;
29572         },        
29573
29574         /**
29575          * Returns true if the message box is currently displayed
29576          * @return {Boolean} True if the message box is visible, else false
29577          */
29578         isVisible : function(){
29579             return dlg && dlg.isVisible();  
29580         },
29581
29582         /**
29583          * Hides the message box if it is displayed
29584          */
29585         hide : function(){
29586             if(this.isVisible()){
29587                 dlg.hide();
29588             }  
29589         },
29590
29591         /**
29592          * Displays a new message box, or reinitializes an existing message box, based on the config options
29593          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29594          * The following config object properties are supported:
29595          * <pre>
29596 Property    Type             Description
29597 ----------  ---------------  ------------------------------------------------------------------------------------
29598 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29599                                    closes (defaults to undefined)
29600 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29601                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29602 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29603                                    progress and wait dialogs will ignore this property and always hide the
29604                                    close button as they can only be closed programmatically.
29605 cls               String           A custom CSS class to apply to the message box element
29606 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29607                                    displayed (defaults to 75)
29608 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29609                                    function will be btn (the name of the button that was clicked, if applicable,
29610                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29611                                    Progress and wait dialogs will ignore this option since they do not respond to
29612                                    user actions and can only be closed programmatically, so any required function
29613                                    should be called by the same code after it closes the dialog.
29614 icon              String           A CSS class that provides a background image to be used as an icon for
29615                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29616 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29617 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29618 modal             Boolean          False to allow user interaction with the page while the message box is
29619                                    displayed (defaults to true)
29620 msg               String           A string that will replace the existing message box body text (defaults
29621                                    to the XHTML-compliant non-breaking space character '&#160;')
29622 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29623 progress          Boolean          True to display a progress bar (defaults to false)
29624 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29625 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29626 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29627 title             String           The title text
29628 value             String           The string value to set into the active textbox element if displayed
29629 wait              Boolean          True to display a progress bar (defaults to false)
29630 width             Number           The width of the dialog in pixels
29631 </pre>
29632          *
29633          * Example usage:
29634          * <pre><code>
29635 Roo.Msg.show({
29636    title: 'Address',
29637    msg: 'Please enter your address:',
29638    width: 300,
29639    buttons: Roo.MessageBox.OKCANCEL,
29640    multiline: true,
29641    fn: saveAddress,
29642    animEl: 'addAddressBtn'
29643 });
29644 </code></pre>
29645          * @param {Object} config Configuration options
29646          * @return {Roo.MessageBox} This message box
29647          */
29648         show : function(options){
29649             if(this.isVisible()){
29650                 this.hide();
29651             }
29652             var d = this.getDialog();
29653             opt = options;
29654             d.setTitle(opt.title || "&#160;");
29655             d.close.setDisplayed(opt.closable !== false);
29656             activeTextEl = textboxEl;
29657             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29658             if(opt.prompt){
29659                 if(opt.multiline){
29660                     textboxEl.hide();
29661                     textareaEl.show();
29662                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29663                         opt.multiline : this.defaultTextHeight);
29664                     activeTextEl = textareaEl;
29665                 }else{
29666                     textboxEl.show();
29667                     textareaEl.hide();
29668                 }
29669             }else{
29670                 textboxEl.hide();
29671                 textareaEl.hide();
29672             }
29673             progressEl.setDisplayed(opt.progress === true);
29674             this.updateProgress(0);
29675             activeTextEl.dom.value = opt.value || "";
29676             if(opt.prompt){
29677                 dlg.setDefaultButton(activeTextEl);
29678             }else{
29679                 var bs = opt.buttons;
29680                 var db = null;
29681                 if(bs && bs.ok){
29682                     db = buttons["ok"];
29683                 }else if(bs && bs.yes){
29684                     db = buttons["yes"];
29685                 }
29686                 dlg.setDefaultButton(db);
29687             }
29688             bwidth = updateButtons(opt.buttons);
29689             this.updateText(opt.msg);
29690             if(opt.cls){
29691                 d.el.addClass(opt.cls);
29692             }
29693             d.proxyDrag = opt.proxyDrag === true;
29694             d.modal = opt.modal !== false;
29695             d.mask = opt.modal !== false ? mask : false;
29696             if(!d.isVisible()){
29697                 // force it to the end of the z-index stack so it gets a cursor in FF
29698                 document.body.appendChild(dlg.el.dom);
29699                 d.animateTarget = null;
29700                 d.show(options.animEl);
29701             }
29702             return this;
29703         },
29704
29705         /**
29706          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29707          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29708          * and closing the message box when the process is complete.
29709          * @param {String} title The title bar text
29710          * @param {String} msg The message box body text
29711          * @return {Roo.MessageBox} This message box
29712          */
29713         progress : function(title, msg){
29714             this.show({
29715                 title : title,
29716                 msg : msg,
29717                 buttons: false,
29718                 progress:true,
29719                 closable:false,
29720                 minWidth: this.minProgressWidth,
29721                 modal : true
29722             });
29723             return this;
29724         },
29725
29726         /**
29727          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29728          * If a callback function is passed it will be called after the user clicks the button, and the
29729          * id of the button that was clicked will be passed as the only parameter to the callback
29730          * (could also be the top-right close button).
29731          * @param {String} title The title bar text
29732          * @param {String} msg The message box body text
29733          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29734          * @param {Object} scope (optional) The scope of the callback function
29735          * @return {Roo.MessageBox} This message box
29736          */
29737         alert : function(title, msg, fn, scope){
29738             this.show({
29739                 title : title,
29740                 msg : msg,
29741                 buttons: this.OK,
29742                 fn: fn,
29743                 scope : scope,
29744                 modal : true
29745             });
29746             return this;
29747         },
29748
29749         /**
29750          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29751          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29752          * You are responsible for closing the message box when the process is complete.
29753          * @param {String} msg The message box body text
29754          * @param {String} title (optional) The title bar text
29755          * @return {Roo.MessageBox} This message box
29756          */
29757         wait : function(msg, title){
29758             this.show({
29759                 title : title,
29760                 msg : msg,
29761                 buttons: false,
29762                 closable:false,
29763                 progress:true,
29764                 modal:true,
29765                 width:300,
29766                 wait:true
29767             });
29768             waitTimer = Roo.TaskMgr.start({
29769                 run: function(i){
29770                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29771                 },
29772                 interval: 1000
29773             });
29774             return this;
29775         },
29776
29777         /**
29778          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29779          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29780          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29781          * @param {String} title The title bar text
29782          * @param {String} msg The message box body text
29783          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29784          * @param {Object} scope (optional) The scope of the callback function
29785          * @return {Roo.MessageBox} This message box
29786          */
29787         confirm : function(title, msg, fn, scope){
29788             this.show({
29789                 title : title,
29790                 msg : msg,
29791                 buttons: this.YESNO,
29792                 fn: fn,
29793                 scope : scope,
29794                 modal : true
29795             });
29796             return this;
29797         },
29798
29799         /**
29800          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29801          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29802          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29803          * (could also be the top-right close button) and the text that was entered will be passed as the two
29804          * parameters to the callback.
29805          * @param {String} title The title bar text
29806          * @param {String} msg The message box body text
29807          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29808          * @param {Object} scope (optional) The scope of the callback function
29809          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29810          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29811          * @return {Roo.MessageBox} This message box
29812          */
29813         prompt : function(title, msg, fn, scope, multiline){
29814             this.show({
29815                 title : title,
29816                 msg : msg,
29817                 buttons: this.OKCANCEL,
29818                 fn: fn,
29819                 minWidth:250,
29820                 scope : scope,
29821                 prompt:true,
29822                 multiline: multiline,
29823                 modal : true
29824             });
29825             return this;
29826         },
29827
29828         /**
29829          * Button config that displays a single OK button
29830          * @type Object
29831          */
29832         OK : {ok:true},
29833         /**
29834          * Button config that displays Yes and No buttons
29835          * @type Object
29836          */
29837         YESNO : {yes:true, no:true},
29838         /**
29839          * Button config that displays OK and Cancel buttons
29840          * @type Object
29841          */
29842         OKCANCEL : {ok:true, cancel:true},
29843         /**
29844          * Button config that displays Yes, No and Cancel buttons
29845          * @type Object
29846          */
29847         YESNOCANCEL : {yes:true, no:true, cancel:true},
29848
29849         /**
29850          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29851          * @type Number
29852          */
29853         defaultTextHeight : 75,
29854         /**
29855          * The maximum width in pixels of the message box (defaults to 600)
29856          * @type Number
29857          */
29858         maxWidth : 600,
29859         /**
29860          * The minimum width in pixels of the message box (defaults to 100)
29861          * @type Number
29862          */
29863         minWidth : 100,
29864         /**
29865          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29866          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29867          * @type Number
29868          */
29869         minProgressWidth : 250,
29870         /**
29871          * An object containing the default button text strings that can be overriden for localized language support.
29872          * Supported properties are: ok, cancel, yes and no.
29873          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29874          * @type Object
29875          */
29876         buttonText : {
29877             ok : "OK",
29878             cancel : "Cancel",
29879             yes : "Yes",
29880             no : "No"
29881         }
29882     };
29883 }();
29884
29885 /**
29886  * Shorthand for {@link Roo.MessageBox}
29887  */
29888 Roo.Msg = Roo.MessageBox;/*
29889  * Based on:
29890  * Ext JS Library 1.1.1
29891  * Copyright(c) 2006-2007, Ext JS, LLC.
29892  *
29893  * Originally Released Under LGPL - original licence link has changed is not relivant.
29894  *
29895  * Fork - LGPL
29896  * <script type="text/javascript">
29897  */
29898 /**
29899  * @class Roo.QuickTips
29900  * Provides attractive and customizable tooltips for any element.
29901  * @singleton
29902  */
29903 Roo.QuickTips = function(){
29904     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
29905     var ce, bd, xy, dd;
29906     var visible = false, disabled = true, inited = false;
29907     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
29908     
29909     var onOver = function(e){
29910         if(disabled){
29911             return;
29912         }
29913         var t = e.getTarget();
29914         if(!t || t.nodeType !== 1 || t == document || t == document.body){
29915             return;
29916         }
29917         if(ce && t == ce.el){
29918             clearTimeout(hideProc);
29919             return;
29920         }
29921         if(t && tagEls[t.id]){
29922             tagEls[t.id].el = t;
29923             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
29924             return;
29925         }
29926         var ttp, et = Roo.fly(t);
29927         var ns = cfg.namespace;
29928         if(tm.interceptTitles && t.title){
29929             ttp = t.title;
29930             t.qtip = ttp;
29931             t.removeAttribute("title");
29932             e.preventDefault();
29933         }else{
29934             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
29935         }
29936         if(ttp){
29937             showProc = show.defer(tm.showDelay, tm, [{
29938                 el: t, 
29939                 text: ttp, 
29940                 width: et.getAttributeNS(ns, cfg.width),
29941                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
29942                 title: et.getAttributeNS(ns, cfg.title),
29943                     cls: et.getAttributeNS(ns, cfg.cls)
29944             }]);
29945         }
29946     };
29947     
29948     var onOut = function(e){
29949         clearTimeout(showProc);
29950         var t = e.getTarget();
29951         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
29952             hideProc = setTimeout(hide, tm.hideDelay);
29953         }
29954     };
29955     
29956     var onMove = function(e){
29957         if(disabled){
29958             return;
29959         }
29960         xy = e.getXY();
29961         xy[1] += 18;
29962         if(tm.trackMouse && ce){
29963             el.setXY(xy);
29964         }
29965     };
29966     
29967     var onDown = function(e){
29968         clearTimeout(showProc);
29969         clearTimeout(hideProc);
29970         if(!e.within(el)){
29971             if(tm.hideOnClick){
29972                 hide();
29973                 tm.disable();
29974                 tm.enable.defer(100, tm);
29975             }
29976         }
29977     };
29978     
29979     var getPad = function(){
29980         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
29981     };
29982
29983     var show = function(o){
29984         if(disabled){
29985             return;
29986         }
29987         clearTimeout(dismissProc);
29988         ce = o;
29989         if(removeCls){ // in case manually hidden
29990             el.removeClass(removeCls);
29991             removeCls = null;
29992         }
29993         if(ce.cls){
29994             el.addClass(ce.cls);
29995             removeCls = ce.cls;
29996         }
29997         if(ce.title){
29998             tipTitle.update(ce.title);
29999             tipTitle.show();
30000         }else{
30001             tipTitle.update('');
30002             tipTitle.hide();
30003         }
30004         el.dom.style.width  = tm.maxWidth+'px';
30005         //tipBody.dom.style.width = '';
30006         tipBodyText.update(o.text);
30007         var p = getPad(), w = ce.width;
30008         if(!w){
30009             var td = tipBodyText.dom;
30010             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30011             if(aw > tm.maxWidth){
30012                 w = tm.maxWidth;
30013             }else if(aw < tm.minWidth){
30014                 w = tm.minWidth;
30015             }else{
30016                 w = aw;
30017             }
30018         }
30019         //tipBody.setWidth(w);
30020         el.setWidth(parseInt(w, 10) + p);
30021         if(ce.autoHide === false){
30022             close.setDisplayed(true);
30023             if(dd){
30024                 dd.unlock();
30025             }
30026         }else{
30027             close.setDisplayed(false);
30028             if(dd){
30029                 dd.lock();
30030             }
30031         }
30032         if(xy){
30033             el.avoidY = xy[1]-18;
30034             el.setXY(xy);
30035         }
30036         if(tm.animate){
30037             el.setOpacity(.1);
30038             el.setStyle("visibility", "visible");
30039             el.fadeIn({callback: afterShow});
30040         }else{
30041             afterShow();
30042         }
30043     };
30044     
30045     var afterShow = function(){
30046         if(ce){
30047             el.show();
30048             esc.enable();
30049             if(tm.autoDismiss && ce.autoHide !== false){
30050                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30051             }
30052         }
30053     };
30054     
30055     var hide = function(noanim){
30056         clearTimeout(dismissProc);
30057         clearTimeout(hideProc);
30058         ce = null;
30059         if(el.isVisible()){
30060             esc.disable();
30061             if(noanim !== true && tm.animate){
30062                 el.fadeOut({callback: afterHide});
30063             }else{
30064                 afterHide();
30065             } 
30066         }
30067     };
30068     
30069     var afterHide = function(){
30070         el.hide();
30071         if(removeCls){
30072             el.removeClass(removeCls);
30073             removeCls = null;
30074         }
30075     };
30076     
30077     return {
30078         /**
30079         * @cfg {Number} minWidth
30080         * The minimum width of the quick tip (defaults to 40)
30081         */
30082        minWidth : 40,
30083         /**
30084         * @cfg {Number} maxWidth
30085         * The maximum width of the quick tip (defaults to 300)
30086         */
30087        maxWidth : 300,
30088         /**
30089         * @cfg {Boolean} interceptTitles
30090         * True to automatically use the element's DOM title value if available (defaults to false)
30091         */
30092        interceptTitles : false,
30093         /**
30094         * @cfg {Boolean} trackMouse
30095         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30096         */
30097        trackMouse : false,
30098         /**
30099         * @cfg {Boolean} hideOnClick
30100         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30101         */
30102        hideOnClick : true,
30103         /**
30104         * @cfg {Number} showDelay
30105         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30106         */
30107        showDelay : 500,
30108         /**
30109         * @cfg {Number} hideDelay
30110         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30111         */
30112        hideDelay : 200,
30113         /**
30114         * @cfg {Boolean} autoHide
30115         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30116         * Used in conjunction with hideDelay.
30117         */
30118        autoHide : true,
30119         /**
30120         * @cfg {Boolean}
30121         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30122         * (defaults to true).  Used in conjunction with autoDismissDelay.
30123         */
30124        autoDismiss : true,
30125         /**
30126         * @cfg {Number}
30127         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30128         */
30129        autoDismissDelay : 5000,
30130        /**
30131         * @cfg {Boolean} animate
30132         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30133         */
30134        animate : false,
30135
30136        /**
30137         * @cfg {String} title
30138         * Title text to display (defaults to '').  This can be any valid HTML markup.
30139         */
30140        /**
30141         * @cfg {String} text
30142         * Body text to display (defaults to '').  This can be any valid HTML markup.
30143         */
30144        /**
30145         * @cfg {String} cls
30146         * A CSS class to apply to the base quick tip element (defaults to '').
30147         */
30148        /**
30149         * @cfg {Number} width
30150         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30151         * minWidth or maxWidth.
30152         */
30153
30154     /**
30155      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30156      * or display QuickTips in a page.
30157      */
30158        init : function(){
30159           tm = Roo.QuickTips;
30160           cfg = tm.tagConfig;
30161           if(!inited){
30162               if(!Roo.isReady){ // allow calling of init() before onReady
30163                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30164                   return;
30165               }
30166               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30167               el.fxDefaults = {stopFx: true};
30168               // maximum custom styling
30169               //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>');
30170               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>');              
30171               tipTitle = el.child('h3');
30172               tipTitle.enableDisplayMode("block");
30173               tipBody = el.child('div.x-tip-bd');
30174               tipBodyText = el.child('div.x-tip-bd-inner');
30175               //bdLeft = el.child('div.x-tip-bd-left');
30176               //bdRight = el.child('div.x-tip-bd-right');
30177               close = el.child('div.x-tip-close');
30178               close.enableDisplayMode("block");
30179               close.on("click", hide);
30180               var d = Roo.get(document);
30181               d.on("mousedown", onDown);
30182               d.on("mouseover", onOver);
30183               d.on("mouseout", onOut);
30184               d.on("mousemove", onMove);
30185               esc = d.addKeyListener(27, hide);
30186               esc.disable();
30187               if(Roo.dd.DD){
30188                   dd = el.initDD("default", null, {
30189                       onDrag : function(){
30190                           el.sync();  
30191                       }
30192                   });
30193                   dd.setHandleElId(tipTitle.id);
30194                   dd.lock();
30195               }
30196               inited = true;
30197           }
30198           this.enable(); 
30199        },
30200
30201     /**
30202      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30203      * are supported:
30204      * <pre>
30205 Property    Type                   Description
30206 ----------  ---------------------  ------------------------------------------------------------------------
30207 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30208      * </ul>
30209      * @param {Object} config The config object
30210      */
30211        register : function(config){
30212            var cs = config instanceof Array ? config : arguments;
30213            for(var i = 0, len = cs.length; i < len; i++) {
30214                var c = cs[i];
30215                var target = c.target;
30216                if(target){
30217                    if(target instanceof Array){
30218                        for(var j = 0, jlen = target.length; j < jlen; j++){
30219                            tagEls[target[j]] = c;
30220                        }
30221                    }else{
30222                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30223                    }
30224                }
30225            }
30226        },
30227
30228     /**
30229      * Removes this quick tip from its element and destroys it.
30230      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30231      */
30232        unregister : function(el){
30233            delete tagEls[Roo.id(el)];
30234        },
30235
30236     /**
30237      * Enable this quick tip.
30238      */
30239        enable : function(){
30240            if(inited && disabled){
30241                locks.pop();
30242                if(locks.length < 1){
30243                    disabled = false;
30244                }
30245            }
30246        },
30247
30248     /**
30249      * Disable this quick tip.
30250      */
30251        disable : function(){
30252           disabled = true;
30253           clearTimeout(showProc);
30254           clearTimeout(hideProc);
30255           clearTimeout(dismissProc);
30256           if(ce){
30257               hide(true);
30258           }
30259           locks.push(1);
30260        },
30261
30262     /**
30263      * Returns true if the quick tip is enabled, else false.
30264      */
30265        isEnabled : function(){
30266             return !disabled;
30267        },
30268
30269         // private
30270        tagConfig : {
30271            namespace : "ext",
30272            attribute : "qtip",
30273            width : "width",
30274            target : "target",
30275            title : "qtitle",
30276            hide : "hide",
30277            cls : "qclass"
30278        }
30279    };
30280 }();
30281
30282 // backwards compat
30283 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30284  * Based on:
30285  * Ext JS Library 1.1.1
30286  * Copyright(c) 2006-2007, Ext JS, LLC.
30287  *
30288  * Originally Released Under LGPL - original licence link has changed is not relivant.
30289  *
30290  * Fork - LGPL
30291  * <script type="text/javascript">
30292  */
30293  
30294
30295 /**
30296  * @class Roo.tree.TreePanel
30297  * @extends Roo.data.Tree
30298
30299  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30300  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30301  * @cfg {Boolean} enableDD true to enable drag and drop
30302  * @cfg {Boolean} enableDrag true to enable just drag
30303  * @cfg {Boolean} enableDrop true to enable just drop
30304  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30305  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30306  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30307  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30308  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30309  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30310  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30311  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30312  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30313  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30314  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30315  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30316  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30317  * @cfg {Function} renderer 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>
30318  * @cfg {Function} rendererTip 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>
30319  * 
30320  * @constructor
30321  * @param {String/HTMLElement/Element} el The container element
30322  * @param {Object} config
30323  */
30324 Roo.tree.TreePanel = function(el, config){
30325     var root = false;
30326     var loader = false;
30327     if (config.root) {
30328         root = config.root;
30329         delete config.root;
30330     }
30331     if (config.loader) {
30332         loader = config.loader;
30333         delete config.loader;
30334     }
30335     
30336     Roo.apply(this, config);
30337     Roo.tree.TreePanel.superclass.constructor.call(this);
30338     this.el = Roo.get(el);
30339     this.el.addClass('x-tree');
30340     //console.log(root);
30341     if (root) {
30342         this.setRootNode( Roo.factory(root, Roo.tree));
30343     }
30344     if (loader) {
30345         this.loader = Roo.factory(loader, Roo.tree);
30346     }
30347    /**
30348     * Read-only. The id of the container element becomes this TreePanel's id.
30349     */
30350    this.id = this.el.id;
30351    this.addEvents({
30352         /**
30353         * @event beforeload
30354         * Fires before a node is loaded, return false to cancel
30355         * @param {Node} node The node being loaded
30356         */
30357         "beforeload" : true,
30358         /**
30359         * @event load
30360         * Fires when a node is loaded
30361         * @param {Node} node The node that was loaded
30362         */
30363         "load" : true,
30364         /**
30365         * @event textchange
30366         * Fires when the text for a node is changed
30367         * @param {Node} node The node
30368         * @param {String} text The new text
30369         * @param {String} oldText The old text
30370         */
30371         "textchange" : true,
30372         /**
30373         * @event beforeexpand
30374         * Fires before a node is expanded, return false to cancel.
30375         * @param {Node} node The node
30376         * @param {Boolean} deep
30377         * @param {Boolean} anim
30378         */
30379         "beforeexpand" : true,
30380         /**
30381         * @event beforecollapse
30382         * Fires before a node is collapsed, return false to cancel.
30383         * @param {Node} node The node
30384         * @param {Boolean} deep
30385         * @param {Boolean} anim
30386         */
30387         "beforecollapse" : true,
30388         /**
30389         * @event expand
30390         * Fires when a node is expanded
30391         * @param {Node} node The node
30392         */
30393         "expand" : true,
30394         /**
30395         * @event disabledchange
30396         * Fires when the disabled status of a node changes
30397         * @param {Node} node The node
30398         * @param {Boolean} disabled
30399         */
30400         "disabledchange" : true,
30401         /**
30402         * @event collapse
30403         * Fires when a node is collapsed
30404         * @param {Node} node The node
30405         */
30406         "collapse" : true,
30407         /**
30408         * @event beforeclick
30409         * Fires before click processing on a node. Return false to cancel the default action.
30410         * @param {Node} node The node
30411         * @param {Roo.EventObject} e The event object
30412         */
30413         "beforeclick":true,
30414         /**
30415         * @event checkchange
30416         * Fires when a node with a checkbox's checked property changes
30417         * @param {Node} this This node
30418         * @param {Boolean} checked
30419         */
30420         "checkchange":true,
30421         /**
30422         * @event click
30423         * Fires when a node is clicked
30424         * @param {Node} node The node
30425         * @param {Roo.EventObject} e The event object
30426         */
30427         "click":true,
30428         /**
30429         * @event dblclick
30430         * Fires when a node is double clicked
30431         * @param {Node} node The node
30432         * @param {Roo.EventObject} e The event object
30433         */
30434         "dblclick":true,
30435         /**
30436         * @event contextmenu
30437         * Fires when a node is right clicked
30438         * @param {Node} node The node
30439         * @param {Roo.EventObject} e The event object
30440         */
30441         "contextmenu":true,
30442         /**
30443         * @event beforechildrenrendered
30444         * Fires right before the child nodes for a node are rendered
30445         * @param {Node} node The node
30446         */
30447         "beforechildrenrendered":true,
30448        /**
30449              * @event startdrag
30450              * Fires when a node starts being dragged
30451              * @param {Roo.tree.TreePanel} this
30452              * @param {Roo.tree.TreeNode} node
30453              * @param {event} e The raw browser event
30454              */ 
30455             "startdrag" : true,
30456             /**
30457              * @event enddrag
30458              * Fires when a drag operation is complete
30459              * @param {Roo.tree.TreePanel} this
30460              * @param {Roo.tree.TreeNode} node
30461              * @param {event} e The raw browser event
30462              */
30463             "enddrag" : true,
30464             /**
30465              * @event dragdrop
30466              * Fires when a dragged node is dropped on a valid DD target
30467              * @param {Roo.tree.TreePanel} this
30468              * @param {Roo.tree.TreeNode} node
30469              * @param {DD} dd The dd it was dropped on
30470              * @param {event} e The raw browser event
30471              */
30472             "dragdrop" : true,
30473             /**
30474              * @event beforenodedrop
30475              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30476              * passed to handlers has the following properties:<br />
30477              * <ul style="padding:5px;padding-left:16px;">
30478              * <li>tree - The TreePanel</li>
30479              * <li>target - The node being targeted for the drop</li>
30480              * <li>data - The drag data from the drag source</li>
30481              * <li>point - The point of the drop - append, above or below</li>
30482              * <li>source - The drag source</li>
30483              * <li>rawEvent - Raw mouse event</li>
30484              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30485              * to be inserted by setting them on this object.</li>
30486              * <li>cancel - Set this to true to cancel the drop.</li>
30487              * </ul>
30488              * @param {Object} dropEvent
30489              */
30490             "beforenodedrop" : true,
30491             /**
30492              * @event nodedrop
30493              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30494              * passed to handlers has the following properties:<br />
30495              * <ul style="padding:5px;padding-left:16px;">
30496              * <li>tree - The TreePanel</li>
30497              * <li>target - The node being targeted for the drop</li>
30498              * <li>data - The drag data from the drag source</li>
30499              * <li>point - The point of the drop - append, above or below</li>
30500              * <li>source - The drag source</li>
30501              * <li>rawEvent - Raw mouse event</li>
30502              * <li>dropNode - Dropped node(s).</li>
30503              * </ul>
30504              * @param {Object} dropEvent
30505              */
30506             "nodedrop" : true,
30507              /**
30508              * @event nodedragover
30509              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30510              * passed to handlers has the following properties:<br />
30511              * <ul style="padding:5px;padding-left:16px;">
30512              * <li>tree - The TreePanel</li>
30513              * <li>target - The node being targeted for the drop</li>
30514              * <li>data - The drag data from the drag source</li>
30515              * <li>point - The point of the drop - append, above or below</li>
30516              * <li>source - The drag source</li>
30517              * <li>rawEvent - Raw mouse event</li>
30518              * <li>dropNode - Drop node(s) provided by the source.</li>
30519              * <li>cancel - Set this to true to signal drop not allowed.</li>
30520              * </ul>
30521              * @param {Object} dragOverEvent
30522              */
30523             "nodedragover" : true
30524         
30525    });
30526    if(this.singleExpand){
30527        this.on("beforeexpand", this.restrictExpand, this);
30528    }
30529 };
30530 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30531     rootVisible : true,
30532     animate: Roo.enableFx,
30533     lines : true,
30534     enableDD : false,
30535     hlDrop : Roo.enableFx,
30536   
30537     renderer: false,
30538     
30539     rendererTip: false,
30540     // private
30541     restrictExpand : function(node){
30542         var p = node.parentNode;
30543         if(p){
30544             if(p.expandedChild && p.expandedChild.parentNode == p){
30545                 p.expandedChild.collapse();
30546             }
30547             p.expandedChild = node;
30548         }
30549     },
30550
30551     // private override
30552     setRootNode : function(node){
30553         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30554         if(!this.rootVisible){
30555             node.ui = new Roo.tree.RootTreeNodeUI(node);
30556         }
30557         return node;
30558     },
30559
30560     /**
30561      * Returns the container element for this TreePanel
30562      */
30563     getEl : function(){
30564         return this.el;
30565     },
30566
30567     /**
30568      * Returns the default TreeLoader for this TreePanel
30569      */
30570     getLoader : function(){
30571         return this.loader;
30572     },
30573
30574     /**
30575      * Expand all nodes
30576      */
30577     expandAll : function(){
30578         this.root.expand(true);
30579     },
30580
30581     /**
30582      * Collapse all nodes
30583      */
30584     collapseAll : function(){
30585         this.root.collapse(true);
30586     },
30587
30588     /**
30589      * Returns the selection model used by this TreePanel
30590      */
30591     getSelectionModel : function(){
30592         if(!this.selModel){
30593             this.selModel = new Roo.tree.DefaultSelectionModel();
30594         }
30595         return this.selModel;
30596     },
30597
30598     /**
30599      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30600      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30601      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30602      * @return {Array}
30603      */
30604     getChecked : function(a, startNode){
30605         startNode = startNode || this.root;
30606         var r = [];
30607         var f = function(){
30608             if(this.attributes.checked){
30609                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30610             }
30611         }
30612         startNode.cascade(f);
30613         return r;
30614     },
30615
30616     /**
30617      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30618      * @param {String} path
30619      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30620      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30621      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30622      */
30623     expandPath : function(path, attr, callback){
30624         attr = attr || "id";
30625         var keys = path.split(this.pathSeparator);
30626         var curNode = this.root;
30627         if(curNode.attributes[attr] != keys[1]){ // invalid root
30628             if(callback){
30629                 callback(false, null);
30630             }
30631             return;
30632         }
30633         var index = 1;
30634         var f = function(){
30635             if(++index == keys.length){
30636                 if(callback){
30637                     callback(true, curNode);
30638                 }
30639                 return;
30640             }
30641             var c = curNode.findChild(attr, keys[index]);
30642             if(!c){
30643                 if(callback){
30644                     callback(false, curNode);
30645                 }
30646                 return;
30647             }
30648             curNode = c;
30649             c.expand(false, false, f);
30650         };
30651         curNode.expand(false, false, f);
30652     },
30653
30654     /**
30655      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30656      * @param {String} path
30657      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30658      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30659      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30660      */
30661     selectPath : function(path, attr, callback){
30662         attr = attr || "id";
30663         var keys = path.split(this.pathSeparator);
30664         var v = keys.pop();
30665         if(keys.length > 0){
30666             var f = function(success, node){
30667                 if(success && node){
30668                     var n = node.findChild(attr, v);
30669                     if(n){
30670                         n.select();
30671                         if(callback){
30672                             callback(true, n);
30673                         }
30674                     }else if(callback){
30675                         callback(false, n);
30676                     }
30677                 }else{
30678                     if(callback){
30679                         callback(false, n);
30680                     }
30681                 }
30682             };
30683             this.expandPath(keys.join(this.pathSeparator), attr, f);
30684         }else{
30685             this.root.select();
30686             if(callback){
30687                 callback(true, this.root);
30688             }
30689         }
30690     },
30691
30692     getTreeEl : function(){
30693         return this.el;
30694     },
30695
30696     /**
30697      * Trigger rendering of this TreePanel
30698      */
30699     render : function(){
30700         if (this.innerCt) {
30701             return this; // stop it rendering more than once!!
30702         }
30703         
30704         this.innerCt = this.el.createChild({tag:"ul",
30705                cls:"x-tree-root-ct " +
30706                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30707
30708         if(this.containerScroll){
30709             Roo.dd.ScrollManager.register(this.el);
30710         }
30711         if((this.enableDD || this.enableDrop) && !this.dropZone){
30712            /**
30713             * The dropZone used by this tree if drop is enabled
30714             * @type Roo.tree.TreeDropZone
30715             */
30716              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30717                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30718            });
30719         }
30720         if((this.enableDD || this.enableDrag) && !this.dragZone){
30721            /**
30722             * The dragZone used by this tree if drag is enabled
30723             * @type Roo.tree.TreeDragZone
30724             */
30725             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30726                ddGroup: this.ddGroup || "TreeDD",
30727                scroll: this.ddScroll
30728            });
30729         }
30730         this.getSelectionModel().init(this);
30731         if (!this.root) {
30732             console.log("ROOT not set in tree");
30733             return;
30734         }
30735         this.root.render();
30736         if(!this.rootVisible){
30737             this.root.renderChildren();
30738         }
30739         return this;
30740     }
30741 });/*
30742  * Based on:
30743  * Ext JS Library 1.1.1
30744  * Copyright(c) 2006-2007, Ext JS, LLC.
30745  *
30746  * Originally Released Under LGPL - original licence link has changed is not relivant.
30747  *
30748  * Fork - LGPL
30749  * <script type="text/javascript">
30750  */
30751  
30752
30753 /**
30754  * @class Roo.tree.DefaultSelectionModel
30755  * @extends Roo.util.Observable
30756  * The default single selection for a TreePanel.
30757  */
30758 Roo.tree.DefaultSelectionModel = function(){
30759    this.selNode = null;
30760    
30761    this.addEvents({
30762        /**
30763         * @event selectionchange
30764         * Fires when the selected node changes
30765         * @param {DefaultSelectionModel} this
30766         * @param {TreeNode} node the new selection
30767         */
30768        "selectionchange" : true,
30769
30770        /**
30771         * @event beforeselect
30772         * Fires before the selected node changes, return false to cancel the change
30773         * @param {DefaultSelectionModel} this
30774         * @param {TreeNode} node the new selection
30775         * @param {TreeNode} node the old selection
30776         */
30777        "beforeselect" : true
30778    });
30779 };
30780
30781 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30782     init : function(tree){
30783         this.tree = tree;
30784         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30785         tree.on("click", this.onNodeClick, this);
30786     },
30787     
30788     onNodeClick : function(node, e){
30789         if (e.ctrlKey && this.selNode == node)  {
30790             this.unselect(node);
30791             return;
30792         }
30793         this.select(node);
30794     },
30795     
30796     /**
30797      * Select a node.
30798      * @param {TreeNode} node The node to select
30799      * @return {TreeNode} The selected node
30800      */
30801     select : function(node){
30802         var last = this.selNode;
30803         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30804             if(last){
30805                 last.ui.onSelectedChange(false);
30806             }
30807             this.selNode = node;
30808             node.ui.onSelectedChange(true);
30809             this.fireEvent("selectionchange", this, node, last);
30810         }
30811         return node;
30812     },
30813     
30814     /**
30815      * Deselect a node.
30816      * @param {TreeNode} node The node to unselect
30817      */
30818     unselect : function(node){
30819         if(this.selNode == node){
30820             this.clearSelections();
30821         }    
30822     },
30823     
30824     /**
30825      * Clear all selections
30826      */
30827     clearSelections : function(){
30828         var n = this.selNode;
30829         if(n){
30830             n.ui.onSelectedChange(false);
30831             this.selNode = null;
30832             this.fireEvent("selectionchange", this, null);
30833         }
30834         return n;
30835     },
30836     
30837     /**
30838      * Get the selected node
30839      * @return {TreeNode} The selected node
30840      */
30841     getSelectedNode : function(){
30842         return this.selNode;    
30843     },
30844     
30845     /**
30846      * Returns true if the node is selected
30847      * @param {TreeNode} node The node to check
30848      * @return {Boolean}
30849      */
30850     isSelected : function(node){
30851         return this.selNode == node;  
30852     },
30853
30854     /**
30855      * Selects the node above the selected node in the tree, intelligently walking the nodes
30856      * @return TreeNode The new selection
30857      */
30858     selectPrevious : function(){
30859         var s = this.selNode || this.lastSelNode;
30860         if(!s){
30861             return null;
30862         }
30863         var ps = s.previousSibling;
30864         if(ps){
30865             if(!ps.isExpanded() || ps.childNodes.length < 1){
30866                 return this.select(ps);
30867             } else{
30868                 var lc = ps.lastChild;
30869                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30870                     lc = lc.lastChild;
30871                 }
30872                 return this.select(lc);
30873             }
30874         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30875             return this.select(s.parentNode);
30876         }
30877         return null;
30878     },
30879
30880     /**
30881      * Selects the node above the selected node in the tree, intelligently walking the nodes
30882      * @return TreeNode The new selection
30883      */
30884     selectNext : function(){
30885         var s = this.selNode || this.lastSelNode;
30886         if(!s){
30887             return null;
30888         }
30889         if(s.firstChild && s.isExpanded()){
30890              return this.select(s.firstChild);
30891          }else if(s.nextSibling){
30892              return this.select(s.nextSibling);
30893          }else if(s.parentNode){
30894             var newS = null;
30895             s.parentNode.bubble(function(){
30896                 if(this.nextSibling){
30897                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
30898                     return false;
30899                 }
30900             });
30901             return newS;
30902          }
30903         return null;
30904     },
30905
30906     onKeyDown : function(e){
30907         var s = this.selNode || this.lastSelNode;
30908         // undesirable, but required
30909         var sm = this;
30910         if(!s){
30911             return;
30912         }
30913         var k = e.getKey();
30914         switch(k){
30915              case e.DOWN:
30916                  e.stopEvent();
30917                  this.selectNext();
30918              break;
30919              case e.UP:
30920                  e.stopEvent();
30921                  this.selectPrevious();
30922              break;
30923              case e.RIGHT:
30924                  e.preventDefault();
30925                  if(s.hasChildNodes()){
30926                      if(!s.isExpanded()){
30927                          s.expand();
30928                      }else if(s.firstChild){
30929                          this.select(s.firstChild, e);
30930                      }
30931                  }
30932              break;
30933              case e.LEFT:
30934                  e.preventDefault();
30935                  if(s.hasChildNodes() && s.isExpanded()){
30936                      s.collapse();
30937                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
30938                      this.select(s.parentNode, e);
30939                  }
30940              break;
30941         };
30942     }
30943 });
30944
30945 /**
30946  * @class Roo.tree.MultiSelectionModel
30947  * @extends Roo.util.Observable
30948  * Multi selection for a TreePanel.
30949  */
30950 Roo.tree.MultiSelectionModel = function(){
30951    this.selNodes = [];
30952    this.selMap = {};
30953    this.addEvents({
30954        /**
30955         * @event selectionchange
30956         * Fires when the selected nodes change
30957         * @param {MultiSelectionModel} this
30958         * @param {Array} nodes Array of the selected nodes
30959         */
30960        "selectionchange" : true
30961    });
30962 };
30963
30964 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
30965     init : function(tree){
30966         this.tree = tree;
30967         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30968         tree.on("click", this.onNodeClick, this);
30969     },
30970     
30971     onNodeClick : function(node, e){
30972         this.select(node, e, e.ctrlKey);
30973     },
30974     
30975     /**
30976      * Select a node.
30977      * @param {TreeNode} node The node to select
30978      * @param {EventObject} e (optional) An event associated with the selection
30979      * @param {Boolean} keepExisting True to retain existing selections
30980      * @return {TreeNode} The selected node
30981      */
30982     select : function(node, e, keepExisting){
30983         if(keepExisting !== true){
30984             this.clearSelections(true);
30985         }
30986         if(this.isSelected(node)){
30987             this.lastSelNode = node;
30988             return node;
30989         }
30990         this.selNodes.push(node);
30991         this.selMap[node.id] = node;
30992         this.lastSelNode = node;
30993         node.ui.onSelectedChange(true);
30994         this.fireEvent("selectionchange", this, this.selNodes);
30995         return node;
30996     },
30997     
30998     /**
30999      * Deselect a node.
31000      * @param {TreeNode} node The node to unselect
31001      */
31002     unselect : function(node){
31003         if(this.selMap[node.id]){
31004             node.ui.onSelectedChange(false);
31005             var sn = this.selNodes;
31006             var index = -1;
31007             if(sn.indexOf){
31008                 index = sn.indexOf(node);
31009             }else{
31010                 for(var i = 0, len = sn.length; i < len; i++){
31011                     if(sn[i] == node){
31012                         index = i;
31013                         break;
31014                     }
31015                 }
31016             }
31017             if(index != -1){
31018                 this.selNodes.splice(index, 1);
31019             }
31020             delete this.selMap[node.id];
31021             this.fireEvent("selectionchange", this, this.selNodes);
31022         }
31023     },
31024     
31025     /**
31026      * Clear all selections
31027      */
31028     clearSelections : function(suppressEvent){
31029         var sn = this.selNodes;
31030         if(sn.length > 0){
31031             for(var i = 0, len = sn.length; i < len; i++){
31032                 sn[i].ui.onSelectedChange(false);
31033             }
31034             this.selNodes = [];
31035             this.selMap = {};
31036             if(suppressEvent !== true){
31037                 this.fireEvent("selectionchange", this, this.selNodes);
31038             }
31039         }
31040     },
31041     
31042     /**
31043      * Returns true if the node is selected
31044      * @param {TreeNode} node The node to check
31045      * @return {Boolean}
31046      */
31047     isSelected : function(node){
31048         return this.selMap[node.id] ? true : false;  
31049     },
31050     
31051     /**
31052      * Returns an array of the selected nodes
31053      * @return {Array}
31054      */
31055     getSelectedNodes : function(){
31056         return this.selNodes;    
31057     },
31058
31059     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31060
31061     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31062
31063     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31064 });/*
31065  * Based on:
31066  * Ext JS Library 1.1.1
31067  * Copyright(c) 2006-2007, Ext JS, LLC.
31068  *
31069  * Originally Released Under LGPL - original licence link has changed is not relivant.
31070  *
31071  * Fork - LGPL
31072  * <script type="text/javascript">
31073  */
31074  
31075 /**
31076  * @class Roo.tree.TreeNode
31077  * @extends Roo.data.Node
31078  * @cfg {String} text The text for this node
31079  * @cfg {Boolean} expanded true to start the node expanded
31080  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31081  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31082  * @cfg {Boolean} disabled true to start the node disabled
31083  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31084  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31085  * @cfg {String} cls A css class to be added to the node
31086  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31087  * @cfg {String} href URL of the link used for the node (defaults to #)
31088  * @cfg {String} hrefTarget target frame for the link
31089  * @cfg {String} qtip An Ext QuickTip for the node
31090  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31091  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31092  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31093  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31094  * (defaults to undefined with no checkbox rendered)
31095  * @constructor
31096  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31097  */
31098 Roo.tree.TreeNode = function(attributes){
31099     attributes = attributes || {};
31100     if(typeof attributes == "string"){
31101         attributes = {text: attributes};
31102     }
31103     this.childrenRendered = false;
31104     this.rendered = false;
31105     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31106     this.expanded = attributes.expanded === true;
31107     this.isTarget = attributes.isTarget !== false;
31108     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31109     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31110
31111     /**
31112      * Read-only. The text for this node. To change it use setText().
31113      * @type String
31114      */
31115     this.text = attributes.text;
31116     /**
31117      * True if this node is disabled.
31118      * @type Boolean
31119      */
31120     this.disabled = attributes.disabled === true;
31121
31122     this.addEvents({
31123         /**
31124         * @event textchange
31125         * Fires when the text for this node is changed
31126         * @param {Node} this This node
31127         * @param {String} text The new text
31128         * @param {String} oldText The old text
31129         */
31130         "textchange" : true,
31131         /**
31132         * @event beforeexpand
31133         * Fires before this node is expanded, return false to cancel.
31134         * @param {Node} this This node
31135         * @param {Boolean} deep
31136         * @param {Boolean} anim
31137         */
31138         "beforeexpand" : true,
31139         /**
31140         * @event beforecollapse
31141         * Fires before this node is collapsed, return false to cancel.
31142         * @param {Node} this This node
31143         * @param {Boolean} deep
31144         * @param {Boolean} anim
31145         */
31146         "beforecollapse" : true,
31147         /**
31148         * @event expand
31149         * Fires when this node is expanded
31150         * @param {Node} this This node
31151         */
31152         "expand" : true,
31153         /**
31154         * @event disabledchange
31155         * Fires when the disabled status of this node changes
31156         * @param {Node} this This node
31157         * @param {Boolean} disabled
31158         */
31159         "disabledchange" : true,
31160         /**
31161         * @event collapse
31162         * Fires when this node is collapsed
31163         * @param {Node} this This node
31164         */
31165         "collapse" : true,
31166         /**
31167         * @event beforeclick
31168         * Fires before click processing. Return false to cancel the default action.
31169         * @param {Node} this This node
31170         * @param {Roo.EventObject} e The event object
31171         */
31172         "beforeclick":true,
31173         /**
31174         * @event checkchange
31175         * Fires when a node with a checkbox's checked property changes
31176         * @param {Node} this This node
31177         * @param {Boolean} checked
31178         */
31179         "checkchange":true,
31180         /**
31181         * @event click
31182         * Fires when this node is clicked
31183         * @param {Node} this This node
31184         * @param {Roo.EventObject} e The event object
31185         */
31186         "click":true,
31187         /**
31188         * @event dblclick
31189         * Fires when this node is double clicked
31190         * @param {Node} this This node
31191         * @param {Roo.EventObject} e The event object
31192         */
31193         "dblclick":true,
31194         /**
31195         * @event contextmenu
31196         * Fires when this node is right clicked
31197         * @param {Node} this This node
31198         * @param {Roo.EventObject} e The event object
31199         */
31200         "contextmenu":true,
31201         /**
31202         * @event beforechildrenrendered
31203         * Fires right before the child nodes for this node are rendered
31204         * @param {Node} this This node
31205         */
31206         "beforechildrenrendered":true
31207     });
31208
31209     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31210
31211     /**
31212      * Read-only. The UI for this node
31213      * @type TreeNodeUI
31214      */
31215     this.ui = new uiClass(this);
31216 };
31217 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31218     preventHScroll: true,
31219     /**
31220      * Returns true if this node is expanded
31221      * @return {Boolean}
31222      */
31223     isExpanded : function(){
31224         return this.expanded;
31225     },
31226
31227     /**
31228      * Returns the UI object for this node
31229      * @return {TreeNodeUI}
31230      */
31231     getUI : function(){
31232         return this.ui;
31233     },
31234
31235     // private override
31236     setFirstChild : function(node){
31237         var of = this.firstChild;
31238         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31239         if(this.childrenRendered && of && node != of){
31240             of.renderIndent(true, true);
31241         }
31242         if(this.rendered){
31243             this.renderIndent(true, true);
31244         }
31245     },
31246
31247     // private override
31248     setLastChild : function(node){
31249         var ol = this.lastChild;
31250         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31251         if(this.childrenRendered && ol && node != ol){
31252             ol.renderIndent(true, true);
31253         }
31254         if(this.rendered){
31255             this.renderIndent(true, true);
31256         }
31257     },
31258
31259     // these methods are overridden to provide lazy rendering support
31260     // private override
31261     appendChild : function(){
31262         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31263         if(node && this.childrenRendered){
31264             node.render();
31265         }
31266         this.ui.updateExpandIcon();
31267         return node;
31268     },
31269
31270     // private override
31271     removeChild : function(node){
31272         this.ownerTree.getSelectionModel().unselect(node);
31273         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31274         // if it's been rendered remove dom node
31275         if(this.childrenRendered){
31276             node.ui.remove();
31277         }
31278         if(this.childNodes.length < 1){
31279             this.collapse(false, false);
31280         }else{
31281             this.ui.updateExpandIcon();
31282         }
31283         if(!this.firstChild) {
31284             this.childrenRendered = false;
31285         }
31286         return node;
31287     },
31288
31289     // private override
31290     insertBefore : function(node, refNode){
31291         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31292         if(newNode && refNode && this.childrenRendered){
31293             node.render();
31294         }
31295         this.ui.updateExpandIcon();
31296         return newNode;
31297     },
31298
31299     /**
31300      * Sets the text for this node
31301      * @param {String} text
31302      */
31303     setText : function(text){
31304         var oldText = this.text;
31305         this.text = text;
31306         this.attributes.text = text;
31307         if(this.rendered){ // event without subscribing
31308             this.ui.onTextChange(this, text, oldText);
31309         }
31310         this.fireEvent("textchange", this, text, oldText);
31311     },
31312
31313     /**
31314      * Triggers selection of this node
31315      */
31316     select : function(){
31317         this.getOwnerTree().getSelectionModel().select(this);
31318     },
31319
31320     /**
31321      * Triggers deselection of this node
31322      */
31323     unselect : function(){
31324         this.getOwnerTree().getSelectionModel().unselect(this);
31325     },
31326
31327     /**
31328      * Returns true if this node is selected
31329      * @return {Boolean}
31330      */
31331     isSelected : function(){
31332         return this.getOwnerTree().getSelectionModel().isSelected(this);
31333     },
31334
31335     /**
31336      * Expand this node.
31337      * @param {Boolean} deep (optional) True to expand all children as well
31338      * @param {Boolean} anim (optional) false to cancel the default animation
31339      * @param {Function} callback (optional) A callback to be called when
31340      * expanding this node completes (does not wait for deep expand to complete).
31341      * Called with 1 parameter, this node.
31342      */
31343     expand : function(deep, anim, callback){
31344         if(!this.expanded){
31345             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31346                 return;
31347             }
31348             if(!this.childrenRendered){
31349                 this.renderChildren();
31350             }
31351             this.expanded = true;
31352             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31353                 this.ui.animExpand(function(){
31354                     this.fireEvent("expand", this);
31355                     if(typeof callback == "function"){
31356                         callback(this);
31357                     }
31358                     if(deep === true){
31359                         this.expandChildNodes(true);
31360                     }
31361                 }.createDelegate(this));
31362                 return;
31363             }else{
31364                 this.ui.expand();
31365                 this.fireEvent("expand", this);
31366                 if(typeof callback == "function"){
31367                     callback(this);
31368                 }
31369             }
31370         }else{
31371            if(typeof callback == "function"){
31372                callback(this);
31373            }
31374         }
31375         if(deep === true){
31376             this.expandChildNodes(true);
31377         }
31378     },
31379
31380     isHiddenRoot : function(){
31381         return this.isRoot && !this.getOwnerTree().rootVisible;
31382     },
31383
31384     /**
31385      * Collapse this node.
31386      * @param {Boolean} deep (optional) True to collapse all children as well
31387      * @param {Boolean} anim (optional) false to cancel the default animation
31388      */
31389     collapse : function(deep, anim){
31390         if(this.expanded && !this.isHiddenRoot()){
31391             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31392                 return;
31393             }
31394             this.expanded = false;
31395             if((this.getOwnerTree().animate && anim !== false) || anim){
31396                 this.ui.animCollapse(function(){
31397                     this.fireEvent("collapse", this);
31398                     if(deep === true){
31399                         this.collapseChildNodes(true);
31400                     }
31401                 }.createDelegate(this));
31402                 return;
31403             }else{
31404                 this.ui.collapse();
31405                 this.fireEvent("collapse", this);
31406             }
31407         }
31408         if(deep === true){
31409             var cs = this.childNodes;
31410             for(var i = 0, len = cs.length; i < len; i++) {
31411                 cs[i].collapse(true, false);
31412             }
31413         }
31414     },
31415
31416     // private
31417     delayedExpand : function(delay){
31418         if(!this.expandProcId){
31419             this.expandProcId = this.expand.defer(delay, this);
31420         }
31421     },
31422
31423     // private
31424     cancelExpand : function(){
31425         if(this.expandProcId){
31426             clearTimeout(this.expandProcId);
31427         }
31428         this.expandProcId = false;
31429     },
31430
31431     /**
31432      * Toggles expanded/collapsed state of the node
31433      */
31434     toggle : function(){
31435         if(this.expanded){
31436             this.collapse();
31437         }else{
31438             this.expand();
31439         }
31440     },
31441
31442     /**
31443      * Ensures all parent nodes are expanded
31444      */
31445     ensureVisible : function(callback){
31446         var tree = this.getOwnerTree();
31447         tree.expandPath(this.parentNode.getPath(), false, function(){
31448             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31449             Roo.callback(callback);
31450         }.createDelegate(this));
31451     },
31452
31453     /**
31454      * Expand all child nodes
31455      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31456      */
31457     expandChildNodes : function(deep){
31458         var cs = this.childNodes;
31459         for(var i = 0, len = cs.length; i < len; i++) {
31460                 cs[i].expand(deep);
31461         }
31462     },
31463
31464     /**
31465      * Collapse all child nodes
31466      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31467      */
31468     collapseChildNodes : function(deep){
31469         var cs = this.childNodes;
31470         for(var i = 0, len = cs.length; i < len; i++) {
31471                 cs[i].collapse(deep);
31472         }
31473     },
31474
31475     /**
31476      * Disables this node
31477      */
31478     disable : function(){
31479         this.disabled = true;
31480         this.unselect();
31481         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31482             this.ui.onDisableChange(this, true);
31483         }
31484         this.fireEvent("disabledchange", this, true);
31485     },
31486
31487     /**
31488      * Enables this node
31489      */
31490     enable : function(){
31491         this.disabled = false;
31492         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31493             this.ui.onDisableChange(this, false);
31494         }
31495         this.fireEvent("disabledchange", this, false);
31496     },
31497
31498     // private
31499     renderChildren : function(suppressEvent){
31500         if(suppressEvent !== false){
31501             this.fireEvent("beforechildrenrendered", this);
31502         }
31503         var cs = this.childNodes;
31504         for(var i = 0, len = cs.length; i < len; i++){
31505             cs[i].render(true);
31506         }
31507         this.childrenRendered = true;
31508     },
31509
31510     // private
31511     sort : function(fn, scope){
31512         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31513         if(this.childrenRendered){
31514             var cs = this.childNodes;
31515             for(var i = 0, len = cs.length; i < len; i++){
31516                 cs[i].render(true);
31517             }
31518         }
31519     },
31520
31521     // private
31522     render : function(bulkRender){
31523         this.ui.render(bulkRender);
31524         if(!this.rendered){
31525             this.rendered = true;
31526             if(this.expanded){
31527                 this.expanded = false;
31528                 this.expand(false, false);
31529             }
31530         }
31531     },
31532
31533     // private
31534     renderIndent : function(deep, refresh){
31535         if(refresh){
31536             this.ui.childIndent = null;
31537         }
31538         this.ui.renderIndent();
31539         if(deep === true && this.childrenRendered){
31540             var cs = this.childNodes;
31541             for(var i = 0, len = cs.length; i < len; i++){
31542                 cs[i].renderIndent(true, refresh);
31543             }
31544         }
31545     }
31546 });/*
31547  * Based on:
31548  * Ext JS Library 1.1.1
31549  * Copyright(c) 2006-2007, Ext JS, LLC.
31550  *
31551  * Originally Released Under LGPL - original licence link has changed is not relivant.
31552  *
31553  * Fork - LGPL
31554  * <script type="text/javascript">
31555  */
31556  
31557 /**
31558  * @class Roo.tree.AsyncTreeNode
31559  * @extends Roo.tree.TreeNode
31560  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31561  * @constructor
31562  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31563  */
31564  Roo.tree.AsyncTreeNode = function(config){
31565     this.loaded = false;
31566     this.loading = false;
31567     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31568     /**
31569     * @event beforeload
31570     * Fires before this node is loaded, return false to cancel
31571     * @param {Node} this This node
31572     */
31573     this.addEvents({'beforeload':true, 'load': true});
31574     /**
31575     * @event load
31576     * Fires when this node is loaded
31577     * @param {Node} this This node
31578     */
31579     /**
31580      * The loader used by this node (defaults to using the tree's defined loader)
31581      * @type TreeLoader
31582      * @property loader
31583      */
31584 };
31585 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31586     expand : function(deep, anim, callback){
31587         if(this.loading){ // if an async load is already running, waiting til it's done
31588             var timer;
31589             var f = function(){
31590                 if(!this.loading){ // done loading
31591                     clearInterval(timer);
31592                     this.expand(deep, anim, callback);
31593                 }
31594             }.createDelegate(this);
31595             timer = setInterval(f, 200);
31596             return;
31597         }
31598         if(!this.loaded){
31599             if(this.fireEvent("beforeload", this) === false){
31600                 return;
31601             }
31602             this.loading = true;
31603             this.ui.beforeLoad(this);
31604             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31605             if(loader){
31606                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31607                 return;
31608             }
31609         }
31610         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31611     },
31612     
31613     /**
31614      * Returns true if this node is currently loading
31615      * @return {Boolean}
31616      */
31617     isLoading : function(){
31618         return this.loading;  
31619     },
31620     
31621     loadComplete : function(deep, anim, callback){
31622         this.loading = false;
31623         this.loaded = true;
31624         this.ui.afterLoad(this);
31625         this.fireEvent("load", this);
31626         this.expand(deep, anim, callback);
31627     },
31628     
31629     /**
31630      * Returns true if this node has been loaded
31631      * @return {Boolean}
31632      */
31633     isLoaded : function(){
31634         return this.loaded;
31635     },
31636     
31637     hasChildNodes : function(){
31638         if(!this.isLeaf() && !this.loaded){
31639             return true;
31640         }else{
31641             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31642         }
31643     },
31644
31645     /**
31646      * Trigger a reload for this node
31647      * @param {Function} callback
31648      */
31649     reload : function(callback){
31650         this.collapse(false, false);
31651         while(this.firstChild){
31652             this.removeChild(this.firstChild);
31653         }
31654         this.childrenRendered = false;
31655         this.loaded = false;
31656         if(this.isHiddenRoot()){
31657             this.expanded = false;
31658         }
31659         this.expand(false, false, callback);
31660     }
31661 });/*
31662  * Based on:
31663  * Ext JS Library 1.1.1
31664  * Copyright(c) 2006-2007, Ext JS, LLC.
31665  *
31666  * Originally Released Under LGPL - original licence link has changed is not relivant.
31667  *
31668  * Fork - LGPL
31669  * <script type="text/javascript">
31670  */
31671  
31672 /**
31673  * @class Roo.tree.TreeNodeUI
31674  * @constructor
31675  * @param {Object} node The node to render
31676  * The TreeNode UI implementation is separate from the
31677  * tree implementation. Unless you are customizing the tree UI,
31678  * you should never have to use this directly.
31679  */
31680 Roo.tree.TreeNodeUI = function(node){
31681     this.node = node;
31682     this.rendered = false;
31683     this.animating = false;
31684     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31685 };
31686
31687 Roo.tree.TreeNodeUI.prototype = {
31688     removeChild : function(node){
31689         if(this.rendered){
31690             this.ctNode.removeChild(node.ui.getEl());
31691         }
31692     },
31693
31694     beforeLoad : function(){
31695          this.addClass("x-tree-node-loading");
31696     },
31697
31698     afterLoad : function(){
31699          this.removeClass("x-tree-node-loading");
31700     },
31701
31702     onTextChange : function(node, text, oldText){
31703         if(this.rendered){
31704             this.textNode.innerHTML = text;
31705         }
31706     },
31707
31708     onDisableChange : function(node, state){
31709         this.disabled = state;
31710         if(state){
31711             this.addClass("x-tree-node-disabled");
31712         }else{
31713             this.removeClass("x-tree-node-disabled");
31714         }
31715     },
31716
31717     onSelectedChange : function(state){
31718         if(state){
31719             this.focus();
31720             this.addClass("x-tree-selected");
31721         }else{
31722             //this.blur();
31723             this.removeClass("x-tree-selected");
31724         }
31725     },
31726
31727     onMove : function(tree, node, oldParent, newParent, index, refNode){
31728         this.childIndent = null;
31729         if(this.rendered){
31730             var targetNode = newParent.ui.getContainer();
31731             if(!targetNode){//target not rendered
31732                 this.holder = document.createElement("div");
31733                 this.holder.appendChild(this.wrap);
31734                 return;
31735             }
31736             var insertBefore = refNode ? refNode.ui.getEl() : null;
31737             if(insertBefore){
31738                 targetNode.insertBefore(this.wrap, insertBefore);
31739             }else{
31740                 targetNode.appendChild(this.wrap);
31741             }
31742             this.node.renderIndent(true);
31743         }
31744     },
31745
31746     addClass : function(cls){
31747         if(this.elNode){
31748             Roo.fly(this.elNode).addClass(cls);
31749         }
31750     },
31751
31752     removeClass : function(cls){
31753         if(this.elNode){
31754             Roo.fly(this.elNode).removeClass(cls);
31755         }
31756     },
31757
31758     remove : function(){
31759         if(this.rendered){
31760             this.holder = document.createElement("div");
31761             this.holder.appendChild(this.wrap);
31762         }
31763     },
31764
31765     fireEvent : function(){
31766         return this.node.fireEvent.apply(this.node, arguments);
31767     },
31768
31769     initEvents : function(){
31770         this.node.on("move", this.onMove, this);
31771         var E = Roo.EventManager;
31772         var a = this.anchor;
31773
31774         var el = Roo.fly(a, '_treeui');
31775
31776         if(Roo.isOpera){ // opera render bug ignores the CSS
31777             el.setStyle("text-decoration", "none");
31778         }
31779
31780         el.on("click", this.onClick, this);
31781         el.on("dblclick", this.onDblClick, this);
31782
31783         if(this.checkbox){
31784             Roo.EventManager.on(this.checkbox,
31785                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31786         }
31787
31788         el.on("contextmenu", this.onContextMenu, this);
31789
31790         var icon = Roo.fly(this.iconNode);
31791         icon.on("click", this.onClick, this);
31792         icon.on("dblclick", this.onDblClick, this);
31793         icon.on("contextmenu", this.onContextMenu, this);
31794         E.on(this.ecNode, "click", this.ecClick, this, true);
31795
31796         if(this.node.disabled){
31797             this.addClass("x-tree-node-disabled");
31798         }
31799         if(this.node.hidden){
31800             this.addClass("x-tree-node-disabled");
31801         }
31802         var ot = this.node.getOwnerTree();
31803         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31804         if(dd && (!this.node.isRoot || ot.rootVisible)){
31805             Roo.dd.Registry.register(this.elNode, {
31806                 node: this.node,
31807                 handles: this.getDDHandles(),
31808                 isHandle: false
31809             });
31810         }
31811     },
31812
31813     getDDHandles : function(){
31814         return [this.iconNode, this.textNode];
31815     },
31816
31817     hide : function(){
31818         if(this.rendered){
31819             this.wrap.style.display = "none";
31820         }
31821     },
31822
31823     show : function(){
31824         if(this.rendered){
31825             this.wrap.style.display = "";
31826         }
31827     },
31828
31829     onContextMenu : function(e){
31830         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31831             e.preventDefault();
31832             this.focus();
31833             this.fireEvent("contextmenu", this.node, e);
31834         }
31835     },
31836
31837     onClick : function(e){
31838         if(this.dropping){
31839             e.stopEvent();
31840             return;
31841         }
31842         if(this.fireEvent("beforeclick", this.node, e) !== false){
31843             if(!this.disabled && this.node.attributes.href){
31844                 this.fireEvent("click", this.node, e);
31845                 return;
31846             }
31847             e.preventDefault();
31848             if(this.disabled){
31849                 return;
31850             }
31851
31852             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31853                 this.node.toggle();
31854             }
31855
31856             this.fireEvent("click", this.node, e);
31857         }else{
31858             e.stopEvent();
31859         }
31860     },
31861
31862     onDblClick : function(e){
31863         e.preventDefault();
31864         if(this.disabled){
31865             return;
31866         }
31867         if(this.checkbox){
31868             this.toggleCheck();
31869         }
31870         if(!this.animating && this.node.hasChildNodes()){
31871             this.node.toggle();
31872         }
31873         this.fireEvent("dblclick", this.node, e);
31874     },
31875
31876     onCheckChange : function(){
31877         var checked = this.checkbox.checked;
31878         this.node.attributes.checked = checked;
31879         this.fireEvent('checkchange', this.node, checked);
31880     },
31881
31882     ecClick : function(e){
31883         if(!this.animating && this.node.hasChildNodes()){
31884             this.node.toggle();
31885         }
31886     },
31887
31888     startDrop : function(){
31889         this.dropping = true;
31890     },
31891
31892     // delayed drop so the click event doesn't get fired on a drop
31893     endDrop : function(){
31894        setTimeout(function(){
31895            this.dropping = false;
31896        }.createDelegate(this), 50);
31897     },
31898
31899     expand : function(){
31900         this.updateExpandIcon();
31901         this.ctNode.style.display = "";
31902     },
31903
31904     focus : function(){
31905         if(!this.node.preventHScroll){
31906             try{this.anchor.focus();
31907             }catch(e){}
31908         }else if(!Roo.isIE){
31909             try{
31910                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
31911                 var l = noscroll.scrollLeft;
31912                 this.anchor.focus();
31913                 noscroll.scrollLeft = l;
31914             }catch(e){}
31915         }
31916     },
31917
31918     toggleCheck : function(value){
31919         var cb = this.checkbox;
31920         if(cb){
31921             cb.checked = (value === undefined ? !cb.checked : value);
31922         }
31923     },
31924
31925     blur : function(){
31926         try{
31927             this.anchor.blur();
31928         }catch(e){}
31929     },
31930
31931     animExpand : function(callback){
31932         var ct = Roo.get(this.ctNode);
31933         ct.stopFx();
31934         if(!this.node.hasChildNodes()){
31935             this.updateExpandIcon();
31936             this.ctNode.style.display = "";
31937             Roo.callback(callback);
31938             return;
31939         }
31940         this.animating = true;
31941         this.updateExpandIcon();
31942
31943         ct.slideIn('t', {
31944            callback : function(){
31945                this.animating = false;
31946                Roo.callback(callback);
31947             },
31948             scope: this,
31949             duration: this.node.ownerTree.duration || .25
31950         });
31951     },
31952
31953     highlight : function(){
31954         var tree = this.node.getOwnerTree();
31955         Roo.fly(this.wrap).highlight(
31956             tree.hlColor || "C3DAF9",
31957             {endColor: tree.hlBaseColor}
31958         );
31959     },
31960
31961     collapse : function(){
31962         this.updateExpandIcon();
31963         this.ctNode.style.display = "none";
31964     },
31965
31966     animCollapse : function(callback){
31967         var ct = Roo.get(this.ctNode);
31968         ct.enableDisplayMode('block');
31969         ct.stopFx();
31970
31971         this.animating = true;
31972         this.updateExpandIcon();
31973
31974         ct.slideOut('t', {
31975             callback : function(){
31976                this.animating = false;
31977                Roo.callback(callback);
31978             },
31979             scope: this,
31980             duration: this.node.ownerTree.duration || .25
31981         });
31982     },
31983
31984     getContainer : function(){
31985         return this.ctNode;
31986     },
31987
31988     getEl : function(){
31989         return this.wrap;
31990     },
31991
31992     appendDDGhost : function(ghostNode){
31993         ghostNode.appendChild(this.elNode.cloneNode(true));
31994     },
31995
31996     getDDRepairXY : function(){
31997         return Roo.lib.Dom.getXY(this.iconNode);
31998     },
31999
32000     onRender : function(){
32001         this.render();
32002     },
32003
32004     render : function(bulkRender){
32005         var n = this.node, a = n.attributes;
32006         var targetNode = n.parentNode ?
32007               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32008
32009         if(!this.rendered){
32010             this.rendered = true;
32011
32012             this.renderElements(n, a, targetNode, bulkRender);
32013
32014             if(a.qtip){
32015                if(this.textNode.setAttributeNS){
32016                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32017                    if(a.qtipTitle){
32018                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32019                    }
32020                }else{
32021                    this.textNode.setAttribute("ext:qtip", a.qtip);
32022                    if(a.qtipTitle){
32023                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32024                    }
32025                }
32026             }else if(a.qtipCfg){
32027                 a.qtipCfg.target = Roo.id(this.textNode);
32028                 Roo.QuickTips.register(a.qtipCfg);
32029             }
32030             this.initEvents();
32031             if(!this.node.expanded){
32032                 this.updateExpandIcon();
32033             }
32034         }else{
32035             if(bulkRender === true) {
32036                 targetNode.appendChild(this.wrap);
32037             }
32038         }
32039     },
32040
32041     renderElements : function(n, a, targetNode, bulkRender){
32042         // add some indent caching, this helps performance when rendering a large tree
32043         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32044         var t = n.getOwnerTree();
32045         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32046         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32047         var cb = typeof a.checked == 'boolean';
32048         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32049         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32050             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32051             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32052             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32053             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32054             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32055              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32056                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32057             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32058             "</li>"];
32059
32060         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32061             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32062                                 n.nextSibling.ui.getEl(), buf.join(""));
32063         }else{
32064             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32065         }
32066
32067         this.elNode = this.wrap.childNodes[0];
32068         this.ctNode = this.wrap.childNodes[1];
32069         var cs = this.elNode.childNodes;
32070         this.indentNode = cs[0];
32071         this.ecNode = cs[1];
32072         this.iconNode = cs[2];
32073         var index = 3;
32074         if(cb){
32075             this.checkbox = cs[3];
32076             index++;
32077         }
32078         this.anchor = cs[index];
32079         this.textNode = cs[index].firstChild;
32080     },
32081
32082     getAnchor : function(){
32083         return this.anchor;
32084     },
32085
32086     getTextEl : function(){
32087         return this.textNode;
32088     },
32089
32090     getIconEl : function(){
32091         return this.iconNode;
32092     },
32093
32094     isChecked : function(){
32095         return this.checkbox ? this.checkbox.checked : false;
32096     },
32097
32098     updateExpandIcon : function(){
32099         if(this.rendered){
32100             var n = this.node, c1, c2;
32101             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32102             var hasChild = n.hasChildNodes();
32103             if(hasChild){
32104                 if(n.expanded){
32105                     cls += "-minus";
32106                     c1 = "x-tree-node-collapsed";
32107                     c2 = "x-tree-node-expanded";
32108                 }else{
32109                     cls += "-plus";
32110                     c1 = "x-tree-node-expanded";
32111                     c2 = "x-tree-node-collapsed";
32112                 }
32113                 if(this.wasLeaf){
32114                     this.removeClass("x-tree-node-leaf");
32115                     this.wasLeaf = false;
32116                 }
32117                 if(this.c1 != c1 || this.c2 != c2){
32118                     Roo.fly(this.elNode).replaceClass(c1, c2);
32119                     this.c1 = c1; this.c2 = c2;
32120                 }
32121             }else{
32122                 if(!this.wasLeaf){
32123                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32124                     delete this.c1;
32125                     delete this.c2;
32126                     this.wasLeaf = true;
32127                 }
32128             }
32129             var ecc = "x-tree-ec-icon "+cls;
32130             if(this.ecc != ecc){
32131                 this.ecNode.className = ecc;
32132                 this.ecc = ecc;
32133             }
32134         }
32135     },
32136
32137     getChildIndent : function(){
32138         if(!this.childIndent){
32139             var buf = [];
32140             var p = this.node;
32141             while(p){
32142                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32143                     if(!p.isLast()) {
32144                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32145                     } else {
32146                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32147                     }
32148                 }
32149                 p = p.parentNode;
32150             }
32151             this.childIndent = buf.join("");
32152         }
32153         return this.childIndent;
32154     },
32155
32156     renderIndent : function(){
32157         if(this.rendered){
32158             var indent = "";
32159             var p = this.node.parentNode;
32160             if(p){
32161                 indent = p.ui.getChildIndent();
32162             }
32163             if(this.indentMarkup != indent){ // don't rerender if not required
32164                 this.indentNode.innerHTML = indent;
32165                 this.indentMarkup = indent;
32166             }
32167             this.updateExpandIcon();
32168         }
32169     }
32170 };
32171
32172 Roo.tree.RootTreeNodeUI = function(){
32173     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32174 };
32175 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32176     render : function(){
32177         if(!this.rendered){
32178             var targetNode = this.node.ownerTree.innerCt.dom;
32179             this.node.expanded = true;
32180             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32181             this.wrap = this.ctNode = targetNode.firstChild;
32182         }
32183     },
32184     collapse : function(){
32185     },
32186     expand : function(){
32187     }
32188 });/*
32189  * Based on:
32190  * Ext JS Library 1.1.1
32191  * Copyright(c) 2006-2007, Ext JS, LLC.
32192  *
32193  * Originally Released Under LGPL - original licence link has changed is not relivant.
32194  *
32195  * Fork - LGPL
32196  * <script type="text/javascript">
32197  */
32198 /**
32199  * @class Roo.tree.TreeLoader
32200  * @extends Roo.util.Observable
32201  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32202  * nodes from a specified URL. The response must be a javascript Array definition
32203  * who's elements are node definition objects. eg:
32204  * <pre><code>
32205    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32206     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32207 </code></pre>
32208  * <br><br>
32209  * A server request is sent, and child nodes are loaded only when a node is expanded.
32210  * The loading node's id is passed to the server under the parameter name "node" to
32211  * enable the server to produce the correct child nodes.
32212  * <br><br>
32213  * To pass extra parameters, an event handler may be attached to the "beforeload"
32214  * event, and the parameters specified in the TreeLoader's baseParams property:
32215  * <pre><code>
32216     myTreeLoader.on("beforeload", function(treeLoader, node) {
32217         this.baseParams.category = node.attributes.category;
32218     }, this);
32219 </code></pre><
32220  * This would pass an HTTP parameter called "category" to the server containing
32221  * the value of the Node's "category" attribute.
32222  * @constructor
32223  * Creates a new Treeloader.
32224  * @param {Object} config A config object containing config properties.
32225  */
32226 Roo.tree.TreeLoader = function(config){
32227     this.baseParams = {};
32228     this.requestMethod = "POST";
32229     Roo.apply(this, config);
32230
32231     this.addEvents({
32232         /**
32233          * @event beforeload
32234          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32235          * @param {Object} This TreeLoader object.
32236          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32237          * @param {Object} callback The callback function specified in the {@link #load} call.
32238          */
32239         "beforeload" : true,
32240         /**
32241          * @event load
32242          * Fires when the node has been successfuly loaded.
32243          * @param {Object} This TreeLoader object.
32244          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32245          * @param {Object} response The response object containing the data from the server.
32246          */
32247         "load" : true,
32248         /**
32249          * @event loadexception
32250          * Fires if the network request failed.
32251          * @param {Object} This TreeLoader object.
32252          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32253          * @param {Object} response The response object containing the data from the server.
32254          */
32255         "loadexception" : true
32256     });
32257
32258     Roo.tree.TreeLoader.superclass.constructor.call(this);
32259 };
32260
32261 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32262     /**
32263     * @cfg {String} dataUrl The URL from which to request a Json string which
32264     * specifies an array of node definition object representing the child nodes
32265     * to be loaded.
32266     */
32267     /**
32268     * @cfg {Object} baseParams (optional) An object containing properties which
32269     * specify HTTP parameters to be passed to each request for child nodes.
32270     */
32271     /**
32272     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32273     * created by this loader. If the attributes sent by the server have an attribute in this object,
32274     * they take priority.
32275     */
32276     /**
32277     * @cfg {Object} uiProviders (optional) An object containing properties which
32278     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32279     * <i>uiProvider</i> attribute of a returned child node is a string rather
32280     * than a reference to a TreeNodeUI implementation, this that string value
32281     * is used as a property name in the uiProviders object. You can define the provider named
32282     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32283     */
32284     uiProviders : {},
32285
32286     /**
32287     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32288     * child nodes before loading.
32289     */
32290     clearOnLoad : true,
32291
32292     /**
32293     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32294     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32295     * Grid query { data : [ .....] }
32296     */
32297     
32298     root : false,
32299      /**
32300     * @cfg {String} queryParam (optional) 
32301     * Name of the query as it will be passed on the querystring (defaults to 'node')
32302     * eg. the request will be ?node=[id]
32303     */
32304     
32305     
32306     queryParam: false,
32307     
32308     /**
32309      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32310      * This is called automatically when a node is expanded, but may be used to reload
32311      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32312      * @param {Roo.tree.TreeNode} node
32313      * @param {Function} callback
32314      */
32315     load : function(node, callback){
32316         if(this.clearOnLoad){
32317             while(node.firstChild){
32318                 node.removeChild(node.firstChild);
32319             }
32320         }
32321         if(node.attributes.children){ // preloaded json children
32322             var cs = node.attributes.children;
32323             for(var i = 0, len = cs.length; i < len; i++){
32324                 node.appendChild(this.createNode(cs[i]));
32325             }
32326             if(typeof callback == "function"){
32327                 callback();
32328             }
32329         }else if(this.dataUrl){
32330             this.requestData(node, callback);
32331         }
32332     },
32333
32334     getParams: function(node){
32335         var buf = [], bp = this.baseParams;
32336         for(var key in bp){
32337             if(typeof bp[key] != "function"){
32338                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32339             }
32340         }
32341         var n = this.queryParam === false ? 'node' : this.queryParam;
32342         buf.push(n + "=", encodeURIComponent(node.id));
32343         return buf.join("");
32344     },
32345
32346     requestData : function(node, callback){
32347         if(this.fireEvent("beforeload", this, node, callback) !== false){
32348             this.transId = Roo.Ajax.request({
32349                 method:this.requestMethod,
32350                 url: this.dataUrl||this.url,
32351                 success: this.handleResponse,
32352                 failure: this.handleFailure,
32353                 scope: this,
32354                 argument: {callback: callback, node: node},
32355                 params: this.getParams(node)
32356             });
32357         }else{
32358             // if the load is cancelled, make sure we notify
32359             // the node that we are done
32360             if(typeof callback == "function"){
32361                 callback();
32362             }
32363         }
32364     },
32365
32366     isLoading : function(){
32367         return this.transId ? true : false;
32368     },
32369
32370     abort : function(){
32371         if(this.isLoading()){
32372             Roo.Ajax.abort(this.transId);
32373         }
32374     },
32375
32376     /**
32377     * Override this function for custom TreeNode node implementation
32378     */
32379     createNode : function(attr){
32380         // apply baseAttrs, nice idea Corey!
32381         if(this.baseAttrs){
32382             Roo.applyIf(attr, this.baseAttrs);
32383         }
32384         if(this.applyLoader !== false){
32385             attr.loader = this;
32386         }
32387         if(typeof(attr.uiProvider) == 'string'){
32388             
32389            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32390                 /**  eval:var:attr */ eval(attr.uiProvider);
32391         }
32392         if(typeof(this.uiProviders['default']) != 'undefined') {
32393             attr.uiProvider = this.uiProviders['default'];
32394         }
32395         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32396         return(attr.leaf ?
32397                         new Roo.tree.TreeNode(attr) :
32398                         new Roo.tree.AsyncTreeNode(attr));
32399     },
32400
32401     processResponse : function(response, node, callback){
32402         var json = response.responseText;
32403         try {
32404             
32405             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32406             if (this.root !== false) {
32407                 o = o[this.root];
32408             }
32409             
32410             for(var i = 0, len = o.length; i < len; i++){
32411                 var n = this.createNode(o[i]);
32412                 if(n){
32413                     node.appendChild(n);
32414                 }
32415             }
32416             if(typeof callback == "function"){
32417                 callback(this, node);
32418             }
32419         }catch(e){
32420             this.handleFailure(response);
32421         }
32422     },
32423
32424     handleResponse : function(response){
32425         this.transId = false;
32426         var a = response.argument;
32427         this.processResponse(response, a.node, a.callback);
32428         this.fireEvent("load", this, a.node, response);
32429     },
32430
32431     handleFailure : function(response){
32432         this.transId = false;
32433         var a = response.argument;
32434         this.fireEvent("loadexception", this, a.node, response);
32435         if(typeof a.callback == "function"){
32436             a.callback(this, a.node);
32437         }
32438     }
32439 });/*
32440  * Based on:
32441  * Ext JS Library 1.1.1
32442  * Copyright(c) 2006-2007, Ext JS, LLC.
32443  *
32444  * Originally Released Under LGPL - original licence link has changed is not relivant.
32445  *
32446  * Fork - LGPL
32447  * <script type="text/javascript">
32448  */
32449
32450 /**
32451 * @class Roo.tree.TreeFilter
32452 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32453 * @param {TreePanel} tree
32454 * @param {Object} config (optional)
32455  */
32456 Roo.tree.TreeFilter = function(tree, config){
32457     this.tree = tree;
32458     this.filtered = {};
32459     Roo.apply(this, config);
32460 };
32461
32462 Roo.tree.TreeFilter.prototype = {
32463     clearBlank:false,
32464     reverse:false,
32465     autoClear:false,
32466     remove:false,
32467
32468      /**
32469      * Filter the data by a specific attribute.
32470      * @param {String/RegExp} value Either string that the attribute value
32471      * should start with or a RegExp to test against the attribute
32472      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32473      * @param {TreeNode} startNode (optional) The node to start the filter at.
32474      */
32475     filter : function(value, attr, startNode){
32476         attr = attr || "text";
32477         var f;
32478         if(typeof value == "string"){
32479             var vlen = value.length;
32480             // auto clear empty filter
32481             if(vlen == 0 && this.clearBlank){
32482                 this.clear();
32483                 return;
32484             }
32485             value = value.toLowerCase();
32486             f = function(n){
32487                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32488             };
32489         }else if(value.exec){ // regex?
32490             f = function(n){
32491                 return value.test(n.attributes[attr]);
32492             };
32493         }else{
32494             throw 'Illegal filter type, must be string or regex';
32495         }
32496         this.filterBy(f, null, startNode);
32497         },
32498
32499     /**
32500      * Filter by a function. The passed function will be called with each
32501      * node in the tree (or from the startNode). If the function returns true, the node is kept
32502      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32503      * @param {Function} fn The filter function
32504      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32505      */
32506     filterBy : function(fn, scope, startNode){
32507         startNode = startNode || this.tree.root;
32508         if(this.autoClear){
32509             this.clear();
32510         }
32511         var af = this.filtered, rv = this.reverse;
32512         var f = function(n){
32513             if(n == startNode){
32514                 return true;
32515             }
32516             if(af[n.id]){
32517                 return false;
32518             }
32519             var m = fn.call(scope || n, n);
32520             if(!m || rv){
32521                 af[n.id] = n;
32522                 n.ui.hide();
32523                 return false;
32524             }
32525             return true;
32526         };
32527         startNode.cascade(f);
32528         if(this.remove){
32529            for(var id in af){
32530                if(typeof id != "function"){
32531                    var n = af[id];
32532                    if(n && n.parentNode){
32533                        n.parentNode.removeChild(n);
32534                    }
32535                }
32536            }
32537         }
32538     },
32539
32540     /**
32541      * Clears the current filter. Note: with the "remove" option
32542      * set a filter cannot be cleared.
32543      */
32544     clear : function(){
32545         var t = this.tree;
32546         var af = this.filtered;
32547         for(var id in af){
32548             if(typeof id != "function"){
32549                 var n = af[id];
32550                 if(n){
32551                     n.ui.show();
32552                 }
32553             }
32554         }
32555         this.filtered = {};
32556     }
32557 };
32558 /*
32559  * Based on:
32560  * Ext JS Library 1.1.1
32561  * Copyright(c) 2006-2007, Ext JS, LLC.
32562  *
32563  * Originally Released Under LGPL - original licence link has changed is not relivant.
32564  *
32565  * Fork - LGPL
32566  * <script type="text/javascript">
32567  */
32568  
32569
32570 /**
32571  * @class Roo.tree.TreeSorter
32572  * Provides sorting of nodes in a TreePanel
32573  * 
32574  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32575  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32576  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32577  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32578  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32579  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32580  * @constructor
32581  * @param {TreePanel} tree
32582  * @param {Object} config
32583  */
32584 Roo.tree.TreeSorter = function(tree, config){
32585     Roo.apply(this, config);
32586     tree.on("beforechildrenrendered", this.doSort, this);
32587     tree.on("append", this.updateSort, this);
32588     tree.on("insert", this.updateSort, this);
32589     
32590     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32591     var p = this.property || "text";
32592     var sortType = this.sortType;
32593     var fs = this.folderSort;
32594     var cs = this.caseSensitive === true;
32595     var leafAttr = this.leafAttr || 'leaf';
32596
32597     this.sortFn = function(n1, n2){
32598         if(fs){
32599             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32600                 return 1;
32601             }
32602             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32603                 return -1;
32604             }
32605         }
32606         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32607         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32608         if(v1 < v2){
32609                         return dsc ? +1 : -1;
32610                 }else if(v1 > v2){
32611                         return dsc ? -1 : +1;
32612         }else{
32613                 return 0;
32614         }
32615     };
32616 };
32617
32618 Roo.tree.TreeSorter.prototype = {
32619     doSort : function(node){
32620         node.sort(this.sortFn);
32621     },
32622     
32623     compareNodes : function(n1, n2){
32624         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32625     },
32626     
32627     updateSort : function(tree, node){
32628         if(node.childrenRendered){
32629             this.doSort.defer(1, this, [node]);
32630         }
32631     }
32632 };/*
32633  * Based on:
32634  * Ext JS Library 1.1.1
32635  * Copyright(c) 2006-2007, Ext JS, LLC.
32636  *
32637  * Originally Released Under LGPL - original licence link has changed is not relivant.
32638  *
32639  * Fork - LGPL
32640  * <script type="text/javascript">
32641  */
32642
32643 if(Roo.dd.DropZone){
32644     
32645 Roo.tree.TreeDropZone = function(tree, config){
32646     this.allowParentInsert = false;
32647     this.allowContainerDrop = false;
32648     this.appendOnly = false;
32649     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32650     this.tree = tree;
32651     this.lastInsertClass = "x-tree-no-status";
32652     this.dragOverData = {};
32653 };
32654
32655 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32656     ddGroup : "TreeDD",
32657     
32658     expandDelay : 1000,
32659     
32660     expandNode : function(node){
32661         if(node.hasChildNodes() && !node.isExpanded()){
32662             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32663         }
32664     },
32665     
32666     queueExpand : function(node){
32667         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32668     },
32669     
32670     cancelExpand : function(){
32671         if(this.expandProcId){
32672             clearTimeout(this.expandProcId);
32673             this.expandProcId = false;
32674         }
32675     },
32676     
32677     isValidDropPoint : function(n, pt, dd, e, data){
32678         if(!n || !data){ return false; }
32679         var targetNode = n.node;
32680         var dropNode = data.node;
32681         // default drop rules
32682         if(!(targetNode && targetNode.isTarget && pt)){
32683             return false;
32684         }
32685         if(pt == "append" && targetNode.allowChildren === false){
32686             return false;
32687         }
32688         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32689             return false;
32690         }
32691         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32692             return false;
32693         }
32694         // reuse the object
32695         var overEvent = this.dragOverData;
32696         overEvent.tree = this.tree;
32697         overEvent.target = targetNode;
32698         overEvent.data = data;
32699         overEvent.point = pt;
32700         overEvent.source = dd;
32701         overEvent.rawEvent = e;
32702         overEvent.dropNode = dropNode;
32703         overEvent.cancel = false;  
32704         var result = this.tree.fireEvent("nodedragover", overEvent);
32705         return overEvent.cancel === false && result !== false;
32706     },
32707     
32708     getDropPoint : function(e, n, dd){
32709         var tn = n.node;
32710         if(tn.isRoot){
32711             return tn.allowChildren !== false ? "append" : false; // always append for root
32712         }
32713         var dragEl = n.ddel;
32714         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32715         var y = Roo.lib.Event.getPageY(e);
32716         var noAppend = tn.allowChildren === false || tn.isLeaf();
32717         if(this.appendOnly || tn.parentNode.allowChildren === false){
32718             return noAppend ? false : "append";
32719         }
32720         var noBelow = false;
32721         if(!this.allowParentInsert){
32722             noBelow = tn.hasChildNodes() && tn.isExpanded();
32723         }
32724         var q = (b - t) / (noAppend ? 2 : 3);
32725         if(y >= t && y < (t + q)){
32726             return "above";
32727         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32728             return "below";
32729         }else{
32730             return "append";
32731         }
32732     },
32733     
32734     onNodeEnter : function(n, dd, e, data){
32735         this.cancelExpand();
32736     },
32737     
32738     onNodeOver : function(n, dd, e, data){
32739         var pt = this.getDropPoint(e, n, dd);
32740         var node = n.node;
32741         
32742         // auto node expand check
32743         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32744             this.queueExpand(node);
32745         }else if(pt != "append"){
32746             this.cancelExpand();
32747         }
32748         
32749         // set the insert point style on the target node
32750         var returnCls = this.dropNotAllowed;
32751         if(this.isValidDropPoint(n, pt, dd, e, data)){
32752            if(pt){
32753                var el = n.ddel;
32754                var cls;
32755                if(pt == "above"){
32756                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32757                    cls = "x-tree-drag-insert-above";
32758                }else if(pt == "below"){
32759                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32760                    cls = "x-tree-drag-insert-below";
32761                }else{
32762                    returnCls = "x-tree-drop-ok-append";
32763                    cls = "x-tree-drag-append";
32764                }
32765                if(this.lastInsertClass != cls){
32766                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32767                    this.lastInsertClass = cls;
32768                }
32769            }
32770        }
32771        return returnCls;
32772     },
32773     
32774     onNodeOut : function(n, dd, e, data){
32775         this.cancelExpand();
32776         this.removeDropIndicators(n);
32777     },
32778     
32779     onNodeDrop : function(n, dd, e, data){
32780         var point = this.getDropPoint(e, n, dd);
32781         var targetNode = n.node;
32782         targetNode.ui.startDrop();
32783         if(!this.isValidDropPoint(n, point, dd, e, data)){
32784             targetNode.ui.endDrop();
32785             return false;
32786         }
32787         // first try to find the drop node
32788         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32789         var dropEvent = {
32790             tree : this.tree,
32791             target: targetNode,
32792             data: data,
32793             point: point,
32794             source: dd,
32795             rawEvent: e,
32796             dropNode: dropNode,
32797             cancel: !dropNode   
32798         };
32799         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32800         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32801             targetNode.ui.endDrop();
32802             return false;
32803         }
32804         // allow target changing
32805         targetNode = dropEvent.target;
32806         if(point == "append" && !targetNode.isExpanded()){
32807             targetNode.expand(false, null, function(){
32808                 this.completeDrop(dropEvent);
32809             }.createDelegate(this));
32810         }else{
32811             this.completeDrop(dropEvent);
32812         }
32813         return true;
32814     },
32815     
32816     completeDrop : function(de){
32817         var ns = de.dropNode, p = de.point, t = de.target;
32818         if(!(ns instanceof Array)){
32819             ns = [ns];
32820         }
32821         var n;
32822         for(var i = 0, len = ns.length; i < len; i++){
32823             n = ns[i];
32824             if(p == "above"){
32825                 t.parentNode.insertBefore(n, t);
32826             }else if(p == "below"){
32827                 t.parentNode.insertBefore(n, t.nextSibling);
32828             }else{
32829                 t.appendChild(n);
32830             }
32831         }
32832         n.ui.focus();
32833         if(this.tree.hlDrop){
32834             n.ui.highlight();
32835         }
32836         t.ui.endDrop();
32837         this.tree.fireEvent("nodedrop", de);
32838     },
32839     
32840     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32841         if(this.tree.hlDrop){
32842             dropNode.ui.focus();
32843             dropNode.ui.highlight();
32844         }
32845         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32846     },
32847     
32848     getTree : function(){
32849         return this.tree;
32850     },
32851     
32852     removeDropIndicators : function(n){
32853         if(n && n.ddel){
32854             var el = n.ddel;
32855             Roo.fly(el).removeClass([
32856                     "x-tree-drag-insert-above",
32857                     "x-tree-drag-insert-below",
32858                     "x-tree-drag-append"]);
32859             this.lastInsertClass = "_noclass";
32860         }
32861     },
32862     
32863     beforeDragDrop : function(target, e, id){
32864         this.cancelExpand();
32865         return true;
32866     },
32867     
32868     afterRepair : function(data){
32869         if(data && Roo.enableFx){
32870             data.node.ui.highlight();
32871         }
32872         this.hideProxy();
32873     }    
32874 });
32875
32876 }/*
32877  * Based on:
32878  * Ext JS Library 1.1.1
32879  * Copyright(c) 2006-2007, Ext JS, LLC.
32880  *
32881  * Originally Released Under LGPL - original licence link has changed is not relivant.
32882  *
32883  * Fork - LGPL
32884  * <script type="text/javascript">
32885  */
32886  
32887
32888 if(Roo.dd.DragZone){
32889 Roo.tree.TreeDragZone = function(tree, config){
32890     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
32891     this.tree = tree;
32892 };
32893
32894 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
32895     ddGroup : "TreeDD",
32896     
32897     onBeforeDrag : function(data, e){
32898         var n = data.node;
32899         return n && n.draggable && !n.disabled;
32900     },
32901     
32902     onInitDrag : function(e){
32903         var data = this.dragData;
32904         this.tree.getSelectionModel().select(data.node);
32905         this.proxy.update("");
32906         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
32907         this.tree.fireEvent("startdrag", this.tree, data.node, e);
32908     },
32909     
32910     getRepairXY : function(e, data){
32911         return data.node.ui.getDDRepairXY();
32912     },
32913     
32914     onEndDrag : function(data, e){
32915         this.tree.fireEvent("enddrag", this.tree, data.node, e);
32916     },
32917     
32918     onValidDrop : function(dd, e, id){
32919         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
32920         this.hideProxy();
32921     },
32922     
32923     beforeInvalidDrop : function(e, id){
32924         // this scrolls the original position back into view
32925         var sm = this.tree.getSelectionModel();
32926         sm.clearSelections();
32927         sm.select(this.dragData.node);
32928     }
32929 });
32930 }/*
32931  * Based on:
32932  * Ext JS Library 1.1.1
32933  * Copyright(c) 2006-2007, Ext JS, LLC.
32934  *
32935  * Originally Released Under LGPL - original licence link has changed is not relivant.
32936  *
32937  * Fork - LGPL
32938  * <script type="text/javascript">
32939  */
32940 /**
32941  * @class Roo.tree.TreeEditor
32942  * @extends Roo.Editor
32943  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
32944  * as the editor field.
32945  * @constructor
32946  * @param {TreePanel} tree
32947  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
32948  */
32949 Roo.tree.TreeEditor = function(tree, config){
32950     config = config || {};
32951     var field = config.events ? config : new Roo.form.TextField(config);
32952     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
32953
32954     this.tree = tree;
32955
32956     tree.on('beforeclick', this.beforeNodeClick, this);
32957     tree.getTreeEl().on('mousedown', this.hide, this);
32958     this.on('complete', this.updateNode, this);
32959     this.on('beforestartedit', this.fitToTree, this);
32960     this.on('startedit', this.bindScroll, this, {delay:10});
32961     this.on('specialkey', this.onSpecialKey, this);
32962 };
32963
32964 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
32965     /**
32966      * @cfg {String} alignment
32967      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
32968      */
32969     alignment: "l-l",
32970     // inherit
32971     autoSize: false,
32972     /**
32973      * @cfg {Boolean} hideEl
32974      * True to hide the bound element while the editor is displayed (defaults to false)
32975      */
32976     hideEl : false,
32977     /**
32978      * @cfg {String} cls
32979      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
32980      */
32981     cls: "x-small-editor x-tree-editor",
32982     /**
32983      * @cfg {Boolean} shim
32984      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
32985      */
32986     shim:false,
32987     // inherit
32988     shadow:"frame",
32989     /**
32990      * @cfg {Number} maxWidth
32991      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
32992      * the containing tree element's size, it will be automatically limited for you to the container width, taking
32993      * scroll and client offsets into account prior to each edit.
32994      */
32995     maxWidth: 250,
32996
32997     editDelay : 350,
32998
32999     // private
33000     fitToTree : function(ed, el){
33001         var td = this.tree.getTreeEl().dom, nd = el.dom;
33002         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33003             td.scrollLeft = nd.offsetLeft;
33004         }
33005         var w = Math.min(
33006                 this.maxWidth,
33007                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33008         this.setSize(w, '');
33009     },
33010
33011     // private
33012     triggerEdit : function(node){
33013         this.completeEdit();
33014         this.editNode = node;
33015         this.startEdit(node.ui.textNode, node.text);
33016     },
33017
33018     // private
33019     bindScroll : function(){
33020         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33021     },
33022
33023     // private
33024     beforeNodeClick : function(node, e){
33025         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33026         this.lastClick = new Date();
33027         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33028             e.stopEvent();
33029             this.triggerEdit(node);
33030             return false;
33031         }
33032     },
33033
33034     // private
33035     updateNode : function(ed, value){
33036         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33037         this.editNode.setText(value);
33038     },
33039
33040     // private
33041     onHide : function(){
33042         Roo.tree.TreeEditor.superclass.onHide.call(this);
33043         if(this.editNode){
33044             this.editNode.ui.focus();
33045         }
33046     },
33047
33048     // private
33049     onSpecialKey : function(field, e){
33050         var k = e.getKey();
33051         if(k == e.ESC){
33052             e.stopEvent();
33053             this.cancelEdit();
33054         }else if(k == e.ENTER && !e.hasModifier()){
33055             e.stopEvent();
33056             this.completeEdit();
33057         }
33058     }
33059 });//<Script type="text/javascript">
33060 /*
33061  * Based on:
33062  * Ext JS Library 1.1.1
33063  * Copyright(c) 2006-2007, Ext JS, LLC.
33064  *
33065  * Originally Released Under LGPL - original licence link has changed is not relivant.
33066  *
33067  * Fork - LGPL
33068  * <script type="text/javascript">
33069  */
33070  
33071 /**
33072  * Not documented??? - probably should be...
33073  */
33074
33075 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33076     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33077     
33078     renderElements : function(n, a, targetNode, bulkRender){
33079         //consel.log("renderElements?");
33080         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33081
33082         var t = n.getOwnerTree();
33083         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33084         
33085         var cols = t.columns;
33086         var bw = t.borderWidth;
33087         var c = cols[0];
33088         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33089          var cb = typeof a.checked == "boolean";
33090         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33091         var colcls = 'x-t-' + tid + '-c0';
33092         var buf = [
33093             '<li class="x-tree-node">',
33094             
33095                 
33096                 '<div class="x-tree-node-el ', a.cls,'">',
33097                     // extran...
33098                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33099                 
33100                 
33101                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33102                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33103                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33104                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33105                            (a.iconCls ? ' '+a.iconCls : ''),
33106                            '" unselectable="on" />',
33107                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33108                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33109                              
33110                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33111                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33112                             '<span unselectable="on" qtip="' + tx + '">',
33113                              tx,
33114                              '</span></a>' ,
33115                     '</div>',
33116                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33117                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33118                  ];
33119         
33120         for(var i = 1, len = cols.length; i < len; i++){
33121             c = cols[i];
33122             colcls = 'x-t-' + tid + '-c' +i;
33123             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33124             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33125                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33126                       "</div>");
33127          }
33128          
33129          buf.push(
33130             '</a>',
33131             '<div class="x-clear"></div></div>',
33132             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33133             "</li>");
33134         
33135         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33136             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33137                                 n.nextSibling.ui.getEl(), buf.join(""));
33138         }else{
33139             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33140         }
33141         var el = this.wrap.firstChild;
33142         this.elRow = el;
33143         this.elNode = el.firstChild;
33144         this.ranchor = el.childNodes[1];
33145         this.ctNode = this.wrap.childNodes[1];
33146         var cs = el.firstChild.childNodes;
33147         this.indentNode = cs[0];
33148         this.ecNode = cs[1];
33149         this.iconNode = cs[2];
33150         var index = 3;
33151         if(cb){
33152             this.checkbox = cs[3];
33153             index++;
33154         }
33155         this.anchor = cs[index];
33156         
33157         this.textNode = cs[index].firstChild;
33158         
33159         //el.on("click", this.onClick, this);
33160         //el.on("dblclick", this.onDblClick, this);
33161         
33162         
33163        // console.log(this);
33164     },
33165     initEvents : function(){
33166         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33167         
33168             
33169         var a = this.ranchor;
33170
33171         var el = Roo.get(a);
33172
33173         if(Roo.isOpera){ // opera render bug ignores the CSS
33174             el.setStyle("text-decoration", "none");
33175         }
33176
33177         el.on("click", this.onClick, this);
33178         el.on("dblclick", this.onDblClick, this);
33179         el.on("contextmenu", this.onContextMenu, this);
33180         
33181     },
33182     
33183     /*onSelectedChange : function(state){
33184         if(state){
33185             this.focus();
33186             this.addClass("x-tree-selected");
33187         }else{
33188             //this.blur();
33189             this.removeClass("x-tree-selected");
33190         }
33191     },*/
33192     addClass : function(cls){
33193         if(this.elRow){
33194             Roo.fly(this.elRow).addClass(cls);
33195         }
33196         
33197     },
33198     
33199     
33200     removeClass : function(cls){
33201         if(this.elRow){
33202             Roo.fly(this.elRow).removeClass(cls);
33203         }
33204     }
33205
33206     
33207     
33208 });//<Script type="text/javascript">
33209
33210 /*
33211  * Based on:
33212  * Ext JS Library 1.1.1
33213  * Copyright(c) 2006-2007, Ext JS, LLC.
33214  *
33215  * Originally Released Under LGPL - original licence link has changed is not relivant.
33216  *
33217  * Fork - LGPL
33218  * <script type="text/javascript">
33219  */
33220  
33221
33222 /**
33223  * @class Roo.tree.ColumnTree
33224  * @extends Roo.data.TreePanel
33225  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33226  * @cfg {int} borderWidth  compined right/left border allowance
33227  * @constructor
33228  * @param {String/HTMLElement/Element} el The container element
33229  * @param {Object} config
33230  */
33231 Roo.tree.ColumnTree =  function(el, config)
33232 {
33233    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33234    this.addEvents({
33235         /**
33236         * @event resize
33237         * Fire this event on a container when it resizes
33238         * @param {int} w Width
33239         * @param {int} h Height
33240         */
33241        "resize" : true
33242     });
33243     this.on('resize', this.onResize, this);
33244 };
33245
33246 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33247     //lines:false,
33248     
33249     
33250     borderWidth: Roo.isBorderBox ? 0 : 2, 
33251     headEls : false,
33252     
33253     render : function(){
33254         // add the header.....
33255        
33256         Roo.tree.ColumnTree.superclass.render.apply(this);
33257         
33258         this.el.addClass('x-column-tree');
33259         
33260         this.headers = this.el.createChild(
33261             {cls:'x-tree-headers'},this.innerCt.dom);
33262    
33263         var cols = this.columns, c;
33264         var totalWidth = 0;
33265         this.headEls = [];
33266         var  len = cols.length;
33267         for(var i = 0; i < len; i++){
33268              c = cols[i];
33269              totalWidth += c.width;
33270             this.headEls.push(this.headers.createChild({
33271                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33272                  cn: {
33273                      cls:'x-tree-hd-text',
33274                      html: c.header
33275                  },
33276                  style:'width:'+(c.width-this.borderWidth)+'px;'
33277              }));
33278         }
33279         this.headers.createChild({cls:'x-clear'});
33280         // prevent floats from wrapping when clipped
33281         this.headers.setWidth(totalWidth);
33282         //this.innerCt.setWidth(totalWidth);
33283         this.innerCt.setStyle({ overflow: 'auto' });
33284         this.onResize(this.width, this.height);
33285              
33286         
33287     },
33288     onResize : function(w,h)
33289     {
33290         this.height = h;
33291         this.width = w;
33292         // resize cols..
33293         this.innerCt.setWidth(this.width);
33294         this.innerCt.setHeight(this.height-20);
33295         
33296         // headers...
33297         var cols = this.columns, c;
33298         var totalWidth = 0;
33299         var expEl = false;
33300         var len = cols.length;
33301         for(var i = 0; i < len; i++){
33302             c = cols[i];
33303             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33304                 // it's the expander..
33305                 expEl  = this.headEls[i];
33306                 continue;
33307             }
33308             totalWidth += c.width;
33309             
33310         }
33311         if (expEl) {
33312             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33313         }
33314         this.headers.setWidth(w-20);
33315
33316         
33317         
33318         
33319     }
33320 });
33321 /*
33322  * Based on:
33323  * Ext JS Library 1.1.1
33324  * Copyright(c) 2006-2007, Ext JS, LLC.
33325  *
33326  * Originally Released Under LGPL - original licence link has changed is not relivant.
33327  *
33328  * Fork - LGPL
33329  * <script type="text/javascript">
33330  */
33331  
33332 /**
33333  * @class Roo.menu.Menu
33334  * @extends Roo.util.Observable
33335  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33336  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33337  * @constructor
33338  * Creates a new Menu
33339  * @param {Object} config Configuration options
33340  */
33341 Roo.menu.Menu = function(config){
33342     Roo.apply(this, config);
33343     this.id = this.id || Roo.id();
33344     this.addEvents({
33345         /**
33346          * @event beforeshow
33347          * Fires before this menu is displayed
33348          * @param {Roo.menu.Menu} this
33349          */
33350         beforeshow : true,
33351         /**
33352          * @event beforehide
33353          * Fires before this menu is hidden
33354          * @param {Roo.menu.Menu} this
33355          */
33356         beforehide : true,
33357         /**
33358          * @event show
33359          * Fires after this menu is displayed
33360          * @param {Roo.menu.Menu} this
33361          */
33362         show : true,
33363         /**
33364          * @event hide
33365          * Fires after this menu is hidden
33366          * @param {Roo.menu.Menu} this
33367          */
33368         hide : true,
33369         /**
33370          * @event click
33371          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33372          * @param {Roo.menu.Menu} this
33373          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33374          * @param {Roo.EventObject} e
33375          */
33376         click : true,
33377         /**
33378          * @event mouseover
33379          * Fires when the mouse is hovering over this menu
33380          * @param {Roo.menu.Menu} this
33381          * @param {Roo.EventObject} e
33382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33383          */
33384         mouseover : true,
33385         /**
33386          * @event mouseout
33387          * Fires when the mouse exits this menu
33388          * @param {Roo.menu.Menu} this
33389          * @param {Roo.EventObject} e
33390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33391          */
33392         mouseout : true,
33393         /**
33394          * @event itemclick
33395          * Fires when a menu item contained in this menu is clicked
33396          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33397          * @param {Roo.EventObject} e
33398          */
33399         itemclick: true
33400     });
33401     if (this.registerMenu) {
33402         Roo.menu.MenuMgr.register(this);
33403     }
33404     
33405     var mis = this.items;
33406     this.items = new Roo.util.MixedCollection();
33407     if(mis){
33408         this.add.apply(this, mis);
33409     }
33410 };
33411
33412 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33413     /**
33414      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33415      */
33416     minWidth : 120,
33417     /**
33418      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33419      * for bottom-right shadow (defaults to "sides")
33420      */
33421     shadow : "sides",
33422     /**
33423      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33424      * this menu (defaults to "tl-tr?")
33425      */
33426     subMenuAlign : "tl-tr?",
33427     /**
33428      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33429      * relative to its element of origin (defaults to "tl-bl?")
33430      */
33431     defaultAlign : "tl-bl?",
33432     /**
33433      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33434      */
33435     allowOtherMenus : false,
33436     /**
33437      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33438      */
33439     registerMenu : true,
33440
33441     hidden:true,
33442
33443     // private
33444     render : function(){
33445         if(this.el){
33446             return;
33447         }
33448         var el = this.el = new Roo.Layer({
33449             cls: "x-menu",
33450             shadow:this.shadow,
33451             constrain: false,
33452             parentEl: this.parentEl || document.body,
33453             zindex:15000
33454         });
33455
33456         this.keyNav = new Roo.menu.MenuNav(this);
33457
33458         if(this.plain){
33459             el.addClass("x-menu-plain");
33460         }
33461         if(this.cls){
33462             el.addClass(this.cls);
33463         }
33464         // generic focus element
33465         this.focusEl = el.createChild({
33466             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33467         });
33468         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33469         ul.on("click", this.onClick, this);
33470         ul.on("mouseover", this.onMouseOver, this);
33471         ul.on("mouseout", this.onMouseOut, this);
33472         this.items.each(function(item){
33473             var li = document.createElement("li");
33474             li.className = "x-menu-list-item";
33475             ul.dom.appendChild(li);
33476             item.render(li, this);
33477         }, this);
33478         this.ul = ul;
33479         this.autoWidth();
33480     },
33481
33482     // private
33483     autoWidth : function(){
33484         var el = this.el, ul = this.ul;
33485         if(!el){
33486             return;
33487         }
33488         var w = this.width;
33489         if(w){
33490             el.setWidth(w);
33491         }else if(Roo.isIE){
33492             el.setWidth(this.minWidth);
33493             var t = el.dom.offsetWidth; // force recalc
33494             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33495         }
33496     },
33497
33498     // private
33499     delayAutoWidth : function(){
33500         if(this.rendered){
33501             if(!this.awTask){
33502                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33503             }
33504             this.awTask.delay(20);
33505         }
33506     },
33507
33508     // private
33509     findTargetItem : function(e){
33510         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33511         if(t && t.menuItemId){
33512             return this.items.get(t.menuItemId);
33513         }
33514     },
33515
33516     // private
33517     onClick : function(e){
33518         var t;
33519         if(t = this.findTargetItem(e)){
33520             t.onClick(e);
33521             this.fireEvent("click", this, t, e);
33522         }
33523     },
33524
33525     // private
33526     setActiveItem : function(item, autoExpand){
33527         if(item != this.activeItem){
33528             if(this.activeItem){
33529                 this.activeItem.deactivate();
33530             }
33531             this.activeItem = item;
33532             item.activate(autoExpand);
33533         }else if(autoExpand){
33534             item.expandMenu();
33535         }
33536     },
33537
33538     // private
33539     tryActivate : function(start, step){
33540         var items = this.items;
33541         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33542             var item = items.get(i);
33543             if(!item.disabled && item.canActivate){
33544                 this.setActiveItem(item, false);
33545                 return item;
33546             }
33547         }
33548         return false;
33549     },
33550
33551     // private
33552     onMouseOver : function(e){
33553         var t;
33554         if(t = this.findTargetItem(e)){
33555             if(t.canActivate && !t.disabled){
33556                 this.setActiveItem(t, true);
33557             }
33558         }
33559         this.fireEvent("mouseover", this, e, t);
33560     },
33561
33562     // private
33563     onMouseOut : function(e){
33564         var t;
33565         if(t = this.findTargetItem(e)){
33566             if(t == this.activeItem && t.shouldDeactivate(e)){
33567                 this.activeItem.deactivate();
33568                 delete this.activeItem;
33569             }
33570         }
33571         this.fireEvent("mouseout", this, e, t);
33572     },
33573
33574     /**
33575      * Read-only.  Returns true if the menu is currently displayed, else false.
33576      * @type Boolean
33577      */
33578     isVisible : function(){
33579         return this.el && !this.hidden;
33580     },
33581
33582     /**
33583      * Displays this menu relative to another element
33584      * @param {String/HTMLElement/Roo.Element} element The element to align to
33585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33586      * the element (defaults to this.defaultAlign)
33587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33588      */
33589     show : function(el, pos, parentMenu){
33590         this.parentMenu = parentMenu;
33591         if(!this.el){
33592             this.render();
33593         }
33594         this.fireEvent("beforeshow", this);
33595         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33596     },
33597
33598     /**
33599      * Displays this menu at a specific xy position
33600      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33601      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33602      */
33603     showAt : function(xy, parentMenu, /* private: */_e){
33604         this.parentMenu = parentMenu;
33605         if(!this.el){
33606             this.render();
33607         }
33608         if(_e !== false){
33609             this.fireEvent("beforeshow", this);
33610             xy = this.el.adjustForConstraints(xy);
33611         }
33612         this.el.setXY(xy);
33613         this.el.show();
33614         this.hidden = false;
33615         this.focus();
33616         this.fireEvent("show", this);
33617     },
33618
33619     focus : function(){
33620         if(!this.hidden){
33621             this.doFocus.defer(50, this);
33622         }
33623     },
33624
33625     doFocus : function(){
33626         if(!this.hidden){
33627             this.focusEl.focus();
33628         }
33629     },
33630
33631     /**
33632      * Hides this menu and optionally all parent menus
33633      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33634      */
33635     hide : function(deep){
33636         if(this.el && this.isVisible()){
33637             this.fireEvent("beforehide", this);
33638             if(this.activeItem){
33639                 this.activeItem.deactivate();
33640                 this.activeItem = null;
33641             }
33642             this.el.hide();
33643             this.hidden = true;
33644             this.fireEvent("hide", this);
33645         }
33646         if(deep === true && this.parentMenu){
33647             this.parentMenu.hide(true);
33648         }
33649     },
33650
33651     /**
33652      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33653      * Any of the following are valid:
33654      * <ul>
33655      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33656      * <li>An HTMLElement object which will be converted to a menu item</li>
33657      * <li>A menu item config object that will be created as a new menu item</li>
33658      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33659      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33660      * </ul>
33661      * Usage:
33662      * <pre><code>
33663 // Create the menu
33664 var menu = new Roo.menu.Menu();
33665
33666 // Create a menu item to add by reference
33667 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33668
33669 // Add a bunch of items at once using different methods.
33670 // Only the last item added will be returned.
33671 var item = menu.add(
33672     menuItem,                // add existing item by ref
33673     'Dynamic Item',          // new TextItem
33674     '-',                     // new separator
33675     { text: 'Config Item' }  // new item by config
33676 );
33677 </code></pre>
33678      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33679      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33680      */
33681     add : function(){
33682         var a = arguments, l = a.length, item;
33683         for(var i = 0; i < l; i++){
33684             var el = a[i];
33685             if(el.render){ // some kind of Item
33686                 item = this.addItem(el);
33687             }else if(typeof el == "string"){ // string
33688                 if(el == "separator" || el == "-"){
33689                     item = this.addSeparator();
33690                 }else{
33691                     item = this.addText(el);
33692                 }
33693             }else if(el.tagName || el.el){ // element
33694                 item = this.addElement(el);
33695             }else if(typeof el == "object"){ // must be menu item config?
33696                 item = this.addMenuItem(el);
33697             }
33698         }
33699         return item;
33700     },
33701
33702     /**
33703      * Returns this menu's underlying {@link Roo.Element} object
33704      * @return {Roo.Element} The element
33705      */
33706     getEl : function(){
33707         if(!this.el){
33708             this.render();
33709         }
33710         return this.el;
33711     },
33712
33713     /**
33714      * Adds a separator bar to the menu
33715      * @return {Roo.menu.Item} The menu item that was added
33716      */
33717     addSeparator : function(){
33718         return this.addItem(new Roo.menu.Separator());
33719     },
33720
33721     /**
33722      * Adds an {@link Roo.Element} object to the menu
33723      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33724      * @return {Roo.menu.Item} The menu item that was added
33725      */
33726     addElement : function(el){
33727         return this.addItem(new Roo.menu.BaseItem(el));
33728     },
33729
33730     /**
33731      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33732      * @param {Roo.menu.Item} item The menu item to add
33733      * @return {Roo.menu.Item} The menu item that was added
33734      */
33735     addItem : function(item){
33736         this.items.add(item);
33737         if(this.ul){
33738             var li = document.createElement("li");
33739             li.className = "x-menu-list-item";
33740             this.ul.dom.appendChild(li);
33741             item.render(li, this);
33742             this.delayAutoWidth();
33743         }
33744         return item;
33745     },
33746
33747     /**
33748      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33749      * @param {Object} config A MenuItem config object
33750      * @return {Roo.menu.Item} The menu item that was added
33751      */
33752     addMenuItem : function(config){
33753         if(!(config instanceof Roo.menu.Item)){
33754             if(typeof config.checked == "boolean"){ // must be check menu item config?
33755                 config = new Roo.menu.CheckItem(config);
33756             }else{
33757                 config = new Roo.menu.Item(config);
33758             }
33759         }
33760         return this.addItem(config);
33761     },
33762
33763     /**
33764      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33765      * @param {String} text The text to display in the menu item
33766      * @return {Roo.menu.Item} The menu item that was added
33767      */
33768     addText : function(text){
33769         return this.addItem(new Roo.menu.TextItem(text));
33770     },
33771
33772     /**
33773      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33774      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33775      * @param {Roo.menu.Item} item The menu item to add
33776      * @return {Roo.menu.Item} The menu item that was added
33777      */
33778     insert : function(index, item){
33779         this.items.insert(index, item);
33780         if(this.ul){
33781             var li = document.createElement("li");
33782             li.className = "x-menu-list-item";
33783             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33784             item.render(li, this);
33785             this.delayAutoWidth();
33786         }
33787         return item;
33788     },
33789
33790     /**
33791      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33792      * @param {Roo.menu.Item} item The menu item to remove
33793      */
33794     remove : function(item){
33795         this.items.removeKey(item.id);
33796         item.destroy();
33797     },
33798
33799     /**
33800      * Removes and destroys all items in the menu
33801      */
33802     removeAll : function(){
33803         var f;
33804         while(f = this.items.first()){
33805             this.remove(f);
33806         }
33807     }
33808 });
33809
33810 // MenuNav is a private utility class used internally by the Menu
33811 Roo.menu.MenuNav = function(menu){
33812     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33813     this.scope = this.menu = menu;
33814 };
33815
33816 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33817     doRelay : function(e, h){
33818         var k = e.getKey();
33819         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33820             this.menu.tryActivate(0, 1);
33821             return false;
33822         }
33823         return h.call(this.scope || this, e, this.menu);
33824     },
33825
33826     up : function(e, m){
33827         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33828             m.tryActivate(m.items.length-1, -1);
33829         }
33830     },
33831
33832     down : function(e, m){
33833         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33834             m.tryActivate(0, 1);
33835         }
33836     },
33837
33838     right : function(e, m){
33839         if(m.activeItem){
33840             m.activeItem.expandMenu(true);
33841         }
33842     },
33843
33844     left : function(e, m){
33845         m.hide();
33846         if(m.parentMenu && m.parentMenu.activeItem){
33847             m.parentMenu.activeItem.activate();
33848         }
33849     },
33850
33851     enter : function(e, m){
33852         if(m.activeItem){
33853             e.stopPropagation();
33854             m.activeItem.onClick(e);
33855             m.fireEvent("click", this, m.activeItem);
33856             return true;
33857         }
33858     }
33859 });/*
33860  * Based on:
33861  * Ext JS Library 1.1.1
33862  * Copyright(c) 2006-2007, Ext JS, LLC.
33863  *
33864  * Originally Released Under LGPL - original licence link has changed is not relivant.
33865  *
33866  * Fork - LGPL
33867  * <script type="text/javascript">
33868  */
33869  
33870 /**
33871  * @class Roo.menu.MenuMgr
33872  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33873  * @singleton
33874  */
33875 Roo.menu.MenuMgr = function(){
33876    var menus, active, groups = {}, attached = false, lastShow = new Date();
33877
33878    // private - called when first menu is created
33879    function init(){
33880        menus = {};
33881        active = new Roo.util.MixedCollection();
33882        Roo.get(document).addKeyListener(27, function(){
33883            if(active.length > 0){
33884                hideAll();
33885            }
33886        });
33887    }
33888
33889    // private
33890    function hideAll(){
33891        if(active && active.length > 0){
33892            var c = active.clone();
33893            c.each(function(m){
33894                m.hide();
33895            });
33896        }
33897    }
33898
33899    // private
33900    function onHide(m){
33901        active.remove(m);
33902        if(active.length < 1){
33903            Roo.get(document).un("mousedown", onMouseDown);
33904            attached = false;
33905        }
33906    }
33907
33908    // private
33909    function onShow(m){
33910        var last = active.last();
33911        lastShow = new Date();
33912        active.add(m);
33913        if(!attached){
33914            Roo.get(document).on("mousedown", onMouseDown);
33915            attached = true;
33916        }
33917        if(m.parentMenu){
33918           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
33919           m.parentMenu.activeChild = m;
33920        }else if(last && last.isVisible()){
33921           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
33922        }
33923    }
33924
33925    // private
33926    function onBeforeHide(m){
33927        if(m.activeChild){
33928            m.activeChild.hide();
33929        }
33930        if(m.autoHideTimer){
33931            clearTimeout(m.autoHideTimer);
33932            delete m.autoHideTimer;
33933        }
33934    }
33935
33936    // private
33937    function onBeforeShow(m){
33938        var pm = m.parentMenu;
33939        if(!pm && !m.allowOtherMenus){
33940            hideAll();
33941        }else if(pm && pm.activeChild && active != m){
33942            pm.activeChild.hide();
33943        }
33944    }
33945
33946    // private
33947    function onMouseDown(e){
33948        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
33949            hideAll();
33950        }
33951    }
33952
33953    // private
33954    function onBeforeCheck(mi, state){
33955        if(state){
33956            var g = groups[mi.group];
33957            for(var i = 0, l = g.length; i < l; i++){
33958                if(g[i] != mi){
33959                    g[i].setChecked(false);
33960                }
33961            }
33962        }
33963    }
33964
33965    return {
33966
33967        /**
33968         * Hides all menus that are currently visible
33969         */
33970        hideAll : function(){
33971             hideAll();  
33972        },
33973
33974        // private
33975        register : function(menu){
33976            if(!menus){
33977                init();
33978            }
33979            menus[menu.id] = menu;
33980            menu.on("beforehide", onBeforeHide);
33981            menu.on("hide", onHide);
33982            menu.on("beforeshow", onBeforeShow);
33983            menu.on("show", onShow);
33984            var g = menu.group;
33985            if(g && menu.events["checkchange"]){
33986                if(!groups[g]){
33987                    groups[g] = [];
33988                }
33989                groups[g].push(menu);
33990                menu.on("checkchange", onCheck);
33991            }
33992        },
33993
33994         /**
33995          * Returns a {@link Roo.menu.Menu} object
33996          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
33997          * be used to generate and return a new Menu instance.
33998          */
33999        get : function(menu){
34000            if(typeof menu == "string"){ // menu id
34001                return menus[menu];
34002            }else if(menu.events){  // menu instance
34003                return menu;
34004            }else if(typeof menu.length == 'number'){ // array of menu items?
34005                return new Roo.menu.Menu({items:menu});
34006            }else{ // otherwise, must be a config
34007                return new Roo.menu.Menu(menu);
34008            }
34009        },
34010
34011        // private
34012        unregister : function(menu){
34013            delete menus[menu.id];
34014            menu.un("beforehide", onBeforeHide);
34015            menu.un("hide", onHide);
34016            menu.un("beforeshow", onBeforeShow);
34017            menu.un("show", onShow);
34018            var g = menu.group;
34019            if(g && menu.events["checkchange"]){
34020                groups[g].remove(menu);
34021                menu.un("checkchange", onCheck);
34022            }
34023        },
34024
34025        // private
34026        registerCheckable : function(menuItem){
34027            var g = menuItem.group;
34028            if(g){
34029                if(!groups[g]){
34030                    groups[g] = [];
34031                }
34032                groups[g].push(menuItem);
34033                menuItem.on("beforecheckchange", onBeforeCheck);
34034            }
34035        },
34036
34037        // private
34038        unregisterCheckable : function(menuItem){
34039            var g = menuItem.group;
34040            if(g){
34041                groups[g].remove(menuItem);
34042                menuItem.un("beforecheckchange", onBeforeCheck);
34043            }
34044        }
34045    };
34046 }();/*
34047  * Based on:
34048  * Ext JS Library 1.1.1
34049  * Copyright(c) 2006-2007, Ext JS, LLC.
34050  *
34051  * Originally Released Under LGPL - original licence link has changed is not relivant.
34052  *
34053  * Fork - LGPL
34054  * <script type="text/javascript">
34055  */
34056  
34057
34058 /**
34059  * @class Roo.menu.BaseItem
34060  * @extends Roo.Component
34061  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34062  * management and base configuration options shared by all menu components.
34063  * @constructor
34064  * Creates a new BaseItem
34065  * @param {Object} config Configuration options
34066  */
34067 Roo.menu.BaseItem = function(config){
34068     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34069
34070     this.addEvents({
34071         /**
34072          * @event click
34073          * Fires when this item is clicked
34074          * @param {Roo.menu.BaseItem} this
34075          * @param {Roo.EventObject} e
34076          */
34077         click: true,
34078         /**
34079          * @event activate
34080          * Fires when this item is activated
34081          * @param {Roo.menu.BaseItem} this
34082          */
34083         activate : true,
34084         /**
34085          * @event deactivate
34086          * Fires when this item is deactivated
34087          * @param {Roo.menu.BaseItem} this
34088          */
34089         deactivate : true
34090     });
34091
34092     if(this.handler){
34093         this.on("click", this.handler, this.scope, true);
34094     }
34095 };
34096
34097 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34098     /**
34099      * @cfg {Function} handler
34100      * A function that will handle the click event of this menu item (defaults to undefined)
34101      */
34102     /**
34103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34104      */
34105     canActivate : false,
34106     /**
34107      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34108      */
34109     activeClass : "x-menu-item-active",
34110     /**
34111      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34112      */
34113     hideOnClick : true,
34114     /**
34115      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34116      */
34117     hideDelay : 100,
34118
34119     // private
34120     ctype: "Roo.menu.BaseItem",
34121
34122     // private
34123     actionMode : "container",
34124
34125     // private
34126     render : function(container, parentMenu){
34127         this.parentMenu = parentMenu;
34128         Roo.menu.BaseItem.superclass.render.call(this, container);
34129         this.container.menuItemId = this.id;
34130     },
34131
34132     // private
34133     onRender : function(container, position){
34134         this.el = Roo.get(this.el);
34135         container.dom.appendChild(this.el.dom);
34136     },
34137
34138     // private
34139     onClick : function(e){
34140         if(!this.disabled && this.fireEvent("click", this, e) !== false
34141                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34142             this.handleClick(e);
34143         }else{
34144             e.stopEvent();
34145         }
34146     },
34147
34148     // private
34149     activate : function(){
34150         if(this.disabled){
34151             return false;
34152         }
34153         var li = this.container;
34154         li.addClass(this.activeClass);
34155         this.region = li.getRegion().adjust(2, 2, -2, -2);
34156         this.fireEvent("activate", this);
34157         return true;
34158     },
34159
34160     // private
34161     deactivate : function(){
34162         this.container.removeClass(this.activeClass);
34163         this.fireEvent("deactivate", this);
34164     },
34165
34166     // private
34167     shouldDeactivate : function(e){
34168         return !this.region || !this.region.contains(e.getPoint());
34169     },
34170
34171     // private
34172     handleClick : function(e){
34173         if(this.hideOnClick){
34174             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34175         }
34176     },
34177
34178     // private
34179     expandMenu : function(autoActivate){
34180         // do nothing
34181     },
34182
34183     // private
34184     hideMenu : function(){
34185         // do nothing
34186     }
34187 });/*
34188  * Based on:
34189  * Ext JS Library 1.1.1
34190  * Copyright(c) 2006-2007, Ext JS, LLC.
34191  *
34192  * Originally Released Under LGPL - original licence link has changed is not relivant.
34193  *
34194  * Fork - LGPL
34195  * <script type="text/javascript">
34196  */
34197  
34198 /**
34199  * @class Roo.menu.Adapter
34200  * @extends Roo.menu.BaseItem
34201  * 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.
34202  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34203  * @constructor
34204  * Creates a new Adapter
34205  * @param {Object} config Configuration options
34206  */
34207 Roo.menu.Adapter = function(component, config){
34208     Roo.menu.Adapter.superclass.constructor.call(this, config);
34209     this.component = component;
34210 };
34211 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34212     // private
34213     canActivate : true,
34214
34215     // private
34216     onRender : function(container, position){
34217         this.component.render(container);
34218         this.el = this.component.getEl();
34219     },
34220
34221     // private
34222     activate : function(){
34223         if(this.disabled){
34224             return false;
34225         }
34226         this.component.focus();
34227         this.fireEvent("activate", this);
34228         return true;
34229     },
34230
34231     // private
34232     deactivate : function(){
34233         this.fireEvent("deactivate", this);
34234     },
34235
34236     // private
34237     disable : function(){
34238         this.component.disable();
34239         Roo.menu.Adapter.superclass.disable.call(this);
34240     },
34241
34242     // private
34243     enable : function(){
34244         this.component.enable();
34245         Roo.menu.Adapter.superclass.enable.call(this);
34246     }
34247 });/*
34248  * Based on:
34249  * Ext JS Library 1.1.1
34250  * Copyright(c) 2006-2007, Ext JS, LLC.
34251  *
34252  * Originally Released Under LGPL - original licence link has changed is not relivant.
34253  *
34254  * Fork - LGPL
34255  * <script type="text/javascript">
34256  */
34257
34258 /**
34259  * @class Roo.menu.TextItem
34260  * @extends Roo.menu.BaseItem
34261  * Adds a static text string to a menu, usually used as either a heading or group separator.
34262  * @constructor
34263  * Creates a new TextItem
34264  * @param {String} text The text to display
34265  */
34266 Roo.menu.TextItem = function(text){
34267     this.text = text;
34268     Roo.menu.TextItem.superclass.constructor.call(this);
34269 };
34270
34271 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34272     /**
34273      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34274      */
34275     hideOnClick : false,
34276     /**
34277      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34278      */
34279     itemCls : "x-menu-text",
34280
34281     // private
34282     onRender : function(){
34283         var s = document.createElement("span");
34284         s.className = this.itemCls;
34285         s.innerHTML = this.text;
34286         this.el = s;
34287         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34288     }
34289 });/*
34290  * Based on:
34291  * Ext JS Library 1.1.1
34292  * Copyright(c) 2006-2007, Ext JS, LLC.
34293  *
34294  * Originally Released Under LGPL - original licence link has changed is not relivant.
34295  *
34296  * Fork - LGPL
34297  * <script type="text/javascript">
34298  */
34299
34300 /**
34301  * @class Roo.menu.Separator
34302  * @extends Roo.menu.BaseItem
34303  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34304  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34305  * @constructor
34306  * @param {Object} config Configuration options
34307  */
34308 Roo.menu.Separator = function(config){
34309     Roo.menu.Separator.superclass.constructor.call(this, config);
34310 };
34311
34312 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34313     /**
34314      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34315      */
34316     itemCls : "x-menu-sep",
34317     /**
34318      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34319      */
34320     hideOnClick : false,
34321
34322     // private
34323     onRender : function(li){
34324         var s = document.createElement("span");
34325         s.className = this.itemCls;
34326         s.innerHTML = "&#160;";
34327         this.el = s;
34328         li.addClass("x-menu-sep-li");
34329         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34330     }
34331 });/*
34332  * Based on:
34333  * Ext JS Library 1.1.1
34334  * Copyright(c) 2006-2007, Ext JS, LLC.
34335  *
34336  * Originally Released Under LGPL - original licence link has changed is not relivant.
34337  *
34338  * Fork - LGPL
34339  * <script type="text/javascript">
34340  */
34341 /**
34342  * @class Roo.menu.Item
34343  * @extends Roo.menu.BaseItem
34344  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34345  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34346  * activation and click handling.
34347  * @constructor
34348  * Creates a new Item
34349  * @param {Object} config Configuration options
34350  */
34351 Roo.menu.Item = function(config){
34352     Roo.menu.Item.superclass.constructor.call(this, config);
34353     if(this.menu){
34354         this.menu = Roo.menu.MenuMgr.get(this.menu);
34355     }
34356 };
34357 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34358     /**
34359      * @cfg {String} icon
34360      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34361      */
34362     /**
34363      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34364      */
34365     itemCls : "x-menu-item",
34366     /**
34367      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34368      */
34369     canActivate : true,
34370     /**
34371      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34372      */
34373     showDelay: 200,
34374     // doc'd in BaseItem
34375     hideDelay: 200,
34376
34377     // private
34378     ctype: "Roo.menu.Item",
34379     
34380     // private
34381     onRender : function(container, position){
34382         var el = document.createElement("a");
34383         el.hideFocus = true;
34384         el.unselectable = "on";
34385         el.href = this.href || "#";
34386         if(this.hrefTarget){
34387             el.target = this.hrefTarget;
34388         }
34389         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34390         el.innerHTML = String.format(
34391                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34392                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34393         this.el = el;
34394         Roo.menu.Item.superclass.onRender.call(this, container, position);
34395     },
34396
34397     /**
34398      * Sets the text to display in this menu item
34399      * @param {String} text The text to display
34400      */
34401     setText : function(text){
34402         this.text = text;
34403         if(this.rendered){
34404             this.el.update(String.format(
34405                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34406                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34407             this.parentMenu.autoWidth();
34408         }
34409     },
34410
34411     // private
34412     handleClick : function(e){
34413         if(!this.href){ // if no link defined, stop the event automatically
34414             e.stopEvent();
34415         }
34416         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34417     },
34418
34419     // private
34420     activate : function(autoExpand){
34421         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34422             this.focus();
34423             if(autoExpand){
34424                 this.expandMenu();
34425             }
34426         }
34427         return true;
34428     },
34429
34430     // private
34431     shouldDeactivate : function(e){
34432         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34433             if(this.menu && this.menu.isVisible()){
34434                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34435             }
34436             return true;
34437         }
34438         return false;
34439     },
34440
34441     // private
34442     deactivate : function(){
34443         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34444         this.hideMenu();
34445     },
34446
34447     // private
34448     expandMenu : function(autoActivate){
34449         if(!this.disabled && this.menu){
34450             clearTimeout(this.hideTimer);
34451             delete this.hideTimer;
34452             if(!this.menu.isVisible() && !this.showTimer){
34453                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34454             }else if (this.menu.isVisible() && autoActivate){
34455                 this.menu.tryActivate(0, 1);
34456             }
34457         }
34458     },
34459
34460     // private
34461     deferExpand : function(autoActivate){
34462         delete this.showTimer;
34463         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34464         if(autoActivate){
34465             this.menu.tryActivate(0, 1);
34466         }
34467     },
34468
34469     // private
34470     hideMenu : function(){
34471         clearTimeout(this.showTimer);
34472         delete this.showTimer;
34473         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34474             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34475         }
34476     },
34477
34478     // private
34479     deferHide : function(){
34480         delete this.hideTimer;
34481         this.menu.hide();
34482     }
34483 });/*
34484  * Based on:
34485  * Ext JS Library 1.1.1
34486  * Copyright(c) 2006-2007, Ext JS, LLC.
34487  *
34488  * Originally Released Under LGPL - original licence link has changed is not relivant.
34489  *
34490  * Fork - LGPL
34491  * <script type="text/javascript">
34492  */
34493  
34494 /**
34495  * @class Roo.menu.CheckItem
34496  * @extends Roo.menu.Item
34497  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34498  * @constructor
34499  * Creates a new CheckItem
34500  * @param {Object} config Configuration options
34501  */
34502 Roo.menu.CheckItem = function(config){
34503     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34504     this.addEvents({
34505         /**
34506          * @event beforecheckchange
34507          * Fires before the checked value is set, providing an opportunity to cancel if needed
34508          * @param {Roo.menu.CheckItem} this
34509          * @param {Boolean} checked The new checked value that will be set
34510          */
34511         "beforecheckchange" : true,
34512         /**
34513          * @event checkchange
34514          * Fires after the checked value has been set
34515          * @param {Roo.menu.CheckItem} this
34516          * @param {Boolean} checked The checked value that was set
34517          */
34518         "checkchange" : true
34519     });
34520     if(this.checkHandler){
34521         this.on('checkchange', this.checkHandler, this.scope);
34522     }
34523 };
34524 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34525     /**
34526      * @cfg {String} group
34527      * All check items with the same group name will automatically be grouped into a single-select
34528      * radio button group (defaults to '')
34529      */
34530     /**
34531      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34532      */
34533     itemCls : "x-menu-item x-menu-check-item",
34534     /**
34535      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34536      */
34537     groupClass : "x-menu-group-item",
34538
34539     /**
34540      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34541      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34542      * initialized with checked = true will be rendered as checked.
34543      */
34544     checked: false,
34545
34546     // private
34547     ctype: "Roo.menu.CheckItem",
34548
34549     // private
34550     onRender : function(c){
34551         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34552         if(this.group){
34553             this.el.addClass(this.groupClass);
34554         }
34555         Roo.menu.MenuMgr.registerCheckable(this);
34556         if(this.checked){
34557             this.checked = false;
34558             this.setChecked(true, true);
34559         }
34560     },
34561
34562     // private
34563     destroy : function(){
34564         if(this.rendered){
34565             Roo.menu.MenuMgr.unregisterCheckable(this);
34566         }
34567         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34568     },
34569
34570     /**
34571      * Set the checked state of this item
34572      * @param {Boolean} checked The new checked value
34573      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34574      */
34575     setChecked : function(state, suppressEvent){
34576         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34577             if(this.container){
34578                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34579             }
34580             this.checked = state;
34581             if(suppressEvent !== true){
34582                 this.fireEvent("checkchange", this, state);
34583             }
34584         }
34585     },
34586
34587     // private
34588     handleClick : function(e){
34589        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34590            this.setChecked(!this.checked);
34591        }
34592        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34593     }
34594 });/*
34595  * Based on:
34596  * Ext JS Library 1.1.1
34597  * Copyright(c) 2006-2007, Ext JS, LLC.
34598  *
34599  * Originally Released Under LGPL - original licence link has changed is not relivant.
34600  *
34601  * Fork - LGPL
34602  * <script type="text/javascript">
34603  */
34604  
34605 /**
34606  * @class Roo.menu.DateItem
34607  * @extends Roo.menu.Adapter
34608  * A menu item that wraps the {@link Roo.DatPicker} component.
34609  * @constructor
34610  * Creates a new DateItem
34611  * @param {Object} config Configuration options
34612  */
34613 Roo.menu.DateItem = function(config){
34614     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34615     /** The Roo.DatePicker object @type Roo.DatePicker */
34616     this.picker = this.component;
34617     this.addEvents({select: true});
34618     
34619     this.picker.on("render", function(picker){
34620         picker.getEl().swallowEvent("click");
34621         picker.container.addClass("x-menu-date-item");
34622     });
34623
34624     this.picker.on("select", this.onSelect, this);
34625 };
34626
34627 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34628     // private
34629     onSelect : function(picker, date){
34630         this.fireEvent("select", this, date, picker);
34631         Roo.menu.DateItem.superclass.handleClick.call(this);
34632     }
34633 });/*
34634  * Based on:
34635  * Ext JS Library 1.1.1
34636  * Copyright(c) 2006-2007, Ext JS, LLC.
34637  *
34638  * Originally Released Under LGPL - original licence link has changed is not relivant.
34639  *
34640  * Fork - LGPL
34641  * <script type="text/javascript">
34642  */
34643  
34644 /**
34645  * @class Roo.menu.ColorItem
34646  * @extends Roo.menu.Adapter
34647  * A menu item that wraps the {@link Roo.ColorPalette} component.
34648  * @constructor
34649  * Creates a new ColorItem
34650  * @param {Object} config Configuration options
34651  */
34652 Roo.menu.ColorItem = function(config){
34653     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34654     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34655     this.palette = this.component;
34656     this.relayEvents(this.palette, ["select"]);
34657     if(this.selectHandler){
34658         this.on('select', this.selectHandler, this.scope);
34659     }
34660 };
34661 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34662  * Based on:
34663  * Ext JS Library 1.1.1
34664  * Copyright(c) 2006-2007, Ext JS, LLC.
34665  *
34666  * Originally Released Under LGPL - original licence link has changed is not relivant.
34667  *
34668  * Fork - LGPL
34669  * <script type="text/javascript">
34670  */
34671  
34672
34673 /**
34674  * @class Roo.menu.DateMenu
34675  * @extends Roo.menu.Menu
34676  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34677  * @constructor
34678  * Creates a new DateMenu
34679  * @param {Object} config Configuration options
34680  */
34681 Roo.menu.DateMenu = function(config){
34682     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34683     this.plain = true;
34684     var di = new Roo.menu.DateItem(config);
34685     this.add(di);
34686     /**
34687      * The {@link Roo.DatePicker} instance for this DateMenu
34688      * @type DatePicker
34689      */
34690     this.picker = di.picker;
34691     /**
34692      * @event select
34693      * @param {DatePicker} picker
34694      * @param {Date} date
34695      */
34696     this.relayEvents(di, ["select"]);
34697
34698     this.on('beforeshow', function(){
34699         if(this.picker){
34700             this.picker.hideMonthPicker(true);
34701         }
34702     }, this);
34703 };
34704 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34705     cls:'x-date-menu'
34706 });/*
34707  * Based on:
34708  * Ext JS Library 1.1.1
34709  * Copyright(c) 2006-2007, Ext JS, LLC.
34710  *
34711  * Originally Released Under LGPL - original licence link has changed is not relivant.
34712  *
34713  * Fork - LGPL
34714  * <script type="text/javascript">
34715  */
34716  
34717
34718 /**
34719  * @class Roo.menu.ColorMenu
34720  * @extends Roo.menu.Menu
34721  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34722  * @constructor
34723  * Creates a new ColorMenu
34724  * @param {Object} config Configuration options
34725  */
34726 Roo.menu.ColorMenu = function(config){
34727     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34728     this.plain = true;
34729     var ci = new Roo.menu.ColorItem(config);
34730     this.add(ci);
34731     /**
34732      * The {@link Roo.ColorPalette} instance for this ColorMenu
34733      * @type ColorPalette
34734      */
34735     this.palette = ci.palette;
34736     /**
34737      * @event select
34738      * @param {ColorPalette} palette
34739      * @param {String} color
34740      */
34741     this.relayEvents(ci, ["select"]);
34742 };
34743 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34744  * Based on:
34745  * Ext JS Library 1.1.1
34746  * Copyright(c) 2006-2007, Ext JS, LLC.
34747  *
34748  * Originally Released Under LGPL - original licence link has changed is not relivant.
34749  *
34750  * Fork - LGPL
34751  * <script type="text/javascript">
34752  */
34753  
34754 /**
34755  * @class Roo.form.Field
34756  * @extends Roo.BoxComponent
34757  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34758  * @constructor
34759  * Creates a new Field
34760  * @param {Object} config Configuration options
34761  */
34762 Roo.form.Field = function(config){
34763     Roo.form.Field.superclass.constructor.call(this, config);
34764 };
34765
34766 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34767     /**
34768      * @cfg {String} fieldLabel Label to use when rendering a form.
34769      */
34770        /**
34771      * @cfg {String} qtip Mouse over tip
34772      */
34773      
34774     /**
34775      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34776      */
34777     invalidClass : "x-form-invalid",
34778     /**
34779      * @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")
34780      */
34781     invalidText : "The value in this field is invalid",
34782     /**
34783      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34784      */
34785     focusClass : "x-form-focus",
34786     /**
34787      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34788       automatic validation (defaults to "keyup").
34789      */
34790     validationEvent : "keyup",
34791     /**
34792      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34793      */
34794     validateOnBlur : true,
34795     /**
34796      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34797      */
34798     validationDelay : 250,
34799     /**
34800      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34801      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34802      */
34803     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34804     /**
34805      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34806      */
34807     fieldClass : "x-form-field",
34808     /**
34809      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34810      *<pre>
34811 Value         Description
34812 -----------   ----------------------------------------------------------------------
34813 qtip          Display a quick tip when the user hovers over the field
34814 title         Display a default browser title attribute popup
34815 under         Add a block div beneath the field containing the error text
34816 side          Add an error icon to the right of the field with a popup on hover
34817 [element id]  Add the error text directly to the innerHTML of the specified element
34818 </pre>
34819      */
34820     msgTarget : 'qtip',
34821     /**
34822      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34823      */
34824     msgFx : 'normal',
34825
34826     /**
34827      * @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.
34828      */
34829     readOnly : false,
34830
34831     /**
34832      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34833      */
34834     disabled : false,
34835
34836     /**
34837      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34838      */
34839     inputType : undefined,
34840     
34841     /**
34842      * @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).
34843          */
34844         tabIndex : undefined,
34845         
34846     // private
34847     isFormField : true,
34848
34849     // private
34850     hasFocus : false,
34851     /**
34852      * @property {Roo.Element} fieldEl
34853      * Element Containing the rendered Field (with label etc.)
34854      */
34855     /**
34856      * @cfg {Mixed} value A value to initialize this field with.
34857      */
34858     value : undefined,
34859
34860     /**
34861      * @cfg {String} name The field's HTML name attribute.
34862      */
34863     /**
34864      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34865      */
34866
34867         // private ??
34868         initComponent : function(){
34869         Roo.form.Field.superclass.initComponent.call(this);
34870         this.addEvents({
34871             /**
34872              * @event focus
34873              * Fires when this field receives input focus.
34874              * @param {Roo.form.Field} this
34875              */
34876             focus : true,
34877             /**
34878              * @event blur
34879              * Fires when this field loses input focus.
34880              * @param {Roo.form.Field} this
34881              */
34882             blur : true,
34883             /**
34884              * @event specialkey
34885              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
34886              * {@link Roo.EventObject#getKey} to determine which key was pressed.
34887              * @param {Roo.form.Field} this
34888              * @param {Roo.EventObject} e The event object
34889              */
34890             specialkey : true,
34891             /**
34892              * @event change
34893              * Fires just before the field blurs if the field value has changed.
34894              * @param {Roo.form.Field} this
34895              * @param {Mixed} newValue The new value
34896              * @param {Mixed} oldValue The original value
34897              */
34898             change : true,
34899             /**
34900              * @event invalid
34901              * Fires after the field has been marked as invalid.
34902              * @param {Roo.form.Field} this
34903              * @param {String} msg The validation message
34904              */
34905             invalid : true,
34906             /**
34907              * @event valid
34908              * Fires after the field has been validated with no errors.
34909              * @param {Roo.form.Field} this
34910              */
34911             valid : true
34912         });
34913     },
34914
34915     /**
34916      * Returns the name attribute of the field if available
34917      * @return {String} name The field name
34918      */
34919     getName: function(){
34920          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
34921     },
34922
34923     // private
34924     onRender : function(ct, position){
34925         Roo.form.Field.superclass.onRender.call(this, ct, position);
34926         if(!this.el){
34927             var cfg = this.getAutoCreate();
34928             if(!cfg.name){
34929                 cfg.name = this.name || this.id;
34930             }
34931             if(this.inputType){
34932                 cfg.type = this.inputType;
34933             }
34934             this.el = ct.createChild(cfg, position);
34935         }
34936         var type = this.el.dom.type;
34937         if(type){
34938             if(type == 'password'){
34939                 type = 'text';
34940             }
34941             this.el.addClass('x-form-'+type);
34942         }
34943         if(this.readOnly){
34944             this.el.dom.readOnly = true;
34945         }
34946         if(this.tabIndex !== undefined){
34947             this.el.dom.setAttribute('tabIndex', this.tabIndex);
34948         }
34949
34950         this.el.addClass([this.fieldClass, this.cls]);
34951         this.initValue();
34952     },
34953
34954     /**
34955      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
34956      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
34957      * @return {Roo.form.Field} this
34958      */
34959     applyTo : function(target){
34960         this.allowDomMove = false;
34961         this.el = Roo.get(target);
34962         this.render(this.el.dom.parentNode);
34963         return this;
34964     },
34965
34966     // private
34967     initValue : function(){
34968         if(this.value !== undefined){
34969             this.setValue(this.value);
34970         }else if(this.el.dom.value.length > 0){
34971             this.setValue(this.el.dom.value);
34972         }
34973     },
34974
34975     /**
34976      * Returns true if this field has been changed since it was originally loaded and is not disabled.
34977      */
34978     isDirty : function() {
34979         if(this.disabled) {
34980             return false;
34981         }
34982         return String(this.getValue()) !== String(this.originalValue);
34983     },
34984
34985     // private
34986     afterRender : function(){
34987         Roo.form.Field.superclass.afterRender.call(this);
34988         this.initEvents();
34989     },
34990
34991     // private
34992     fireKey : function(e){
34993         if(e.isNavKeyPress()){
34994             this.fireEvent("specialkey", this, e);
34995         }
34996     },
34997
34998     /**
34999      * Resets the current field value to the originally loaded value and clears any validation messages
35000      */
35001     reset : function(){
35002         this.setValue(this.originalValue);
35003         this.clearInvalid();
35004     },
35005
35006     // private
35007     initEvents : function(){
35008         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35009         this.el.on("focus", this.onFocus,  this);
35010         this.el.on("blur", this.onBlur,  this);
35011
35012         // reference to original value for reset
35013         this.originalValue = this.getValue();
35014     },
35015
35016     // private
35017     onFocus : function(){
35018         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35019             this.el.addClass(this.focusClass);
35020         }
35021         if(!this.hasFocus){
35022             this.hasFocus = true;
35023             this.startValue = this.getValue();
35024             this.fireEvent("focus", this);
35025         }
35026     },
35027
35028     beforeBlur : Roo.emptyFn,
35029
35030     // private
35031     onBlur : function(){
35032         this.beforeBlur();
35033         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35034             this.el.removeClass(this.focusClass);
35035         }
35036         this.hasFocus = false;
35037         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35038             this.validate();
35039         }
35040         var v = this.getValue();
35041         if(String(v) !== String(this.startValue)){
35042             this.fireEvent('change', this, v, this.startValue);
35043         }
35044         this.fireEvent("blur", this);
35045     },
35046
35047     /**
35048      * Returns whether or not the field value is currently valid
35049      * @param {Boolean} preventMark True to disable marking the field invalid
35050      * @return {Boolean} True if the value is valid, else false
35051      */
35052     isValid : function(preventMark){
35053         if(this.disabled){
35054             return true;
35055         }
35056         var restore = this.preventMark;
35057         this.preventMark = preventMark === true;
35058         var v = this.validateValue(this.processValue(this.getRawValue()));
35059         this.preventMark = restore;
35060         return v;
35061     },
35062
35063     /**
35064      * Validates the field value
35065      * @return {Boolean} True if the value is valid, else false
35066      */
35067     validate : function(){
35068         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35069             this.clearInvalid();
35070             return true;
35071         }
35072         return false;
35073     },
35074
35075     processValue : function(value){
35076         return value;
35077     },
35078
35079     // private
35080     // Subclasses should provide the validation implementation by overriding this
35081     validateValue : function(value){
35082         return true;
35083     },
35084
35085     /**
35086      * Mark this field as invalid
35087      * @param {String} msg The validation message
35088      */
35089     markInvalid : function(msg){
35090         if(!this.rendered || this.preventMark){ // not rendered
35091             return;
35092         }
35093         this.el.addClass(this.invalidClass);
35094         msg = msg || this.invalidText;
35095         switch(this.msgTarget){
35096             case 'qtip':
35097                 this.el.dom.qtip = msg;
35098                 this.el.dom.qclass = 'x-form-invalid-tip';
35099                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35100                     Roo.QuickTips.enable();
35101                 }
35102                 break;
35103             case 'title':
35104                 this.el.dom.title = msg;
35105                 break;
35106             case 'under':
35107                 if(!this.errorEl){
35108                     var elp = this.el.findParent('.x-form-element', 5, true);
35109                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35110                     this.errorEl.setWidth(elp.getWidth(true)-20);
35111                 }
35112                 this.errorEl.update(msg);
35113                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35114                 break;
35115             case 'side':
35116                 if(!this.errorIcon){
35117                     var elp = this.el.findParent('.x-form-element', 5, true);
35118                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35119                 }
35120                 this.alignErrorIcon();
35121                 this.errorIcon.dom.qtip = msg;
35122                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35123                 this.errorIcon.show();
35124                 this.on('resize', this.alignErrorIcon, this);
35125                 break;
35126             default:
35127                 var t = Roo.getDom(this.msgTarget);
35128                 t.innerHTML = msg;
35129                 t.style.display = this.msgDisplay;
35130                 break;
35131         }
35132         this.fireEvent('invalid', this, msg);
35133     },
35134
35135     // private
35136     alignErrorIcon : function(){
35137         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35138     },
35139
35140     /**
35141      * Clear any invalid styles/messages for this field
35142      */
35143     clearInvalid : function(){
35144         if(!this.rendered || this.preventMark){ // not rendered
35145             return;
35146         }
35147         this.el.removeClass(this.invalidClass);
35148         switch(this.msgTarget){
35149             case 'qtip':
35150                 this.el.dom.qtip = '';
35151                 break;
35152             case 'title':
35153                 this.el.dom.title = '';
35154                 break;
35155             case 'under':
35156                 if(this.errorEl){
35157                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35158                 }
35159                 break;
35160             case 'side':
35161                 if(this.errorIcon){
35162                     this.errorIcon.dom.qtip = '';
35163                     this.errorIcon.hide();
35164                     this.un('resize', this.alignErrorIcon, this);
35165                 }
35166                 break;
35167             default:
35168                 var t = Roo.getDom(this.msgTarget);
35169                 t.innerHTML = '';
35170                 t.style.display = 'none';
35171                 break;
35172         }
35173         this.fireEvent('valid', this);
35174     },
35175
35176     /**
35177      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35178      * @return {Mixed} value The field value
35179      */
35180     getRawValue : function(){
35181         var v = this.el.getValue();
35182         if(v === this.emptyText){
35183             v = '';
35184         }
35185         return v;
35186     },
35187
35188     /**
35189      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35190      * @return {Mixed} value The field value
35191      */
35192     getValue : function(){
35193         var v = this.el.getValue();
35194         if(v === this.emptyText || v === undefined){
35195             v = '';
35196         }
35197         return v;
35198     },
35199
35200     /**
35201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35202      * @param {Mixed} value The value to set
35203      */
35204     setRawValue : function(v){
35205         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35206     },
35207
35208     /**
35209      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35210      * @param {Mixed} value The value to set
35211      */
35212     setValue : function(v){
35213         this.value = v;
35214         if(this.rendered){
35215             this.el.dom.value = (v === null || v === undefined ? '' : v);
35216             this.validate();
35217         }
35218     },
35219
35220     adjustSize : function(w, h){
35221         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35222         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35223         return s;
35224     },
35225
35226     adjustWidth : function(tag, w){
35227         tag = tag.toLowerCase();
35228         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35229             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35230                 if(tag == 'input'){
35231                     return w + 2;
35232                 }
35233                 if(tag = 'textarea'){
35234                     return w-2;
35235                 }
35236             }else if(Roo.isOpera){
35237                 if(tag == 'input'){
35238                     return w + 2;
35239                 }
35240                 if(tag = 'textarea'){
35241                     return w-2;
35242                 }
35243             }
35244         }
35245         return w;
35246     }
35247 });
35248
35249
35250 // anything other than normal should be considered experimental
35251 Roo.form.Field.msgFx = {
35252     normal : {
35253         show: function(msgEl, f){
35254             msgEl.setDisplayed('block');
35255         },
35256
35257         hide : function(msgEl, f){
35258             msgEl.setDisplayed(false).update('');
35259         }
35260     },
35261
35262     slide : {
35263         show: function(msgEl, f){
35264             msgEl.slideIn('t', {stopFx:true});
35265         },
35266
35267         hide : function(msgEl, f){
35268             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35269         }
35270     },
35271
35272     slideRight : {
35273         show: function(msgEl, f){
35274             msgEl.fixDisplay();
35275             msgEl.alignTo(f.el, 'tl-tr');
35276             msgEl.slideIn('l', {stopFx:true});
35277         },
35278
35279         hide : function(msgEl, f){
35280             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35281         }
35282     }
35283 };/*
35284  * Based on:
35285  * Ext JS Library 1.1.1
35286  * Copyright(c) 2006-2007, Ext JS, LLC.
35287  *
35288  * Originally Released Under LGPL - original licence link has changed is not relivant.
35289  *
35290  * Fork - LGPL
35291  * <script type="text/javascript">
35292  */
35293  
35294
35295 /**
35296  * @class Roo.form.TextField
35297  * @extends Roo.form.Field
35298  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35299  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35300  * @constructor
35301  * Creates a new TextField
35302  * @param {Object} config Configuration options
35303  */
35304 Roo.form.TextField = function(config){
35305     Roo.form.TextField.superclass.constructor.call(this, config);
35306     this.addEvents({
35307         /**
35308          * @event autosize
35309          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35310          * according to the default logic, but this event provides a hook for the developer to apply additional
35311          * logic at runtime to resize the field if needed.
35312              * @param {Roo.form.Field} this This text field
35313              * @param {Number} width The new field width
35314              */
35315         autosize : true
35316     });
35317 };
35318
35319 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35320     /**
35321      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35322      */
35323     grow : false,
35324     /**
35325      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35326      */
35327     growMin : 30,
35328     /**
35329      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35330      */
35331     growMax : 800,
35332     /**
35333      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35334      */
35335     vtype : null,
35336     /**
35337      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35338      */
35339     maskRe : null,
35340     /**
35341      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35342      */
35343     disableKeyFilter : false,
35344     /**
35345      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35346      */
35347     allowBlank : true,
35348     /**
35349      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35350      */
35351     minLength : 0,
35352     /**
35353      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35354      */
35355     maxLength : Number.MAX_VALUE,
35356     /**
35357      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35358      */
35359     minLengthText : "The minimum length for this field is {0}",
35360     /**
35361      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35362      */
35363     maxLengthText : "The maximum length for this field is {0}",
35364     /**
35365      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35366      */
35367     selectOnFocus : false,
35368     /**
35369      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35370      */
35371     blankText : "This field is required",
35372     /**
35373      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35374      * If available, this function will be called only after the basic validators all return true, and will be passed the
35375      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35376      */
35377     validator : null,
35378     /**
35379      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35380      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35381      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35382      */
35383     regex : null,
35384     /**
35385      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35386      */
35387     regexText : "",
35388     /**
35389      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35390      */
35391     emptyText : null,
35392     /**
35393      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35394      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35395      */
35396     emptyClass : 'x-form-empty-field',
35397
35398     // private
35399     initEvents : function(){
35400         Roo.form.TextField.superclass.initEvents.call(this);
35401         if(this.validationEvent == 'keyup'){
35402             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35403             this.el.on('keyup', this.filterValidation, this);
35404         }
35405         else if(this.validationEvent !== false){
35406             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35407         }
35408         if(this.selectOnFocus || this.emptyText){
35409             this.on("focus", this.preFocus, this);
35410             if(this.emptyText){
35411                 this.on('blur', this.postBlur, this);
35412                 this.applyEmptyText();
35413             }
35414         }
35415         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35416             this.el.on("keypress", this.filterKeys, this);
35417         }
35418         if(this.grow){
35419             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35420             this.el.on("click", this.autoSize,  this);
35421         }
35422     },
35423
35424     processValue : function(value){
35425         if(this.stripCharsRe){
35426             var newValue = value.replace(this.stripCharsRe, '');
35427             if(newValue !== value){
35428                 this.setRawValue(newValue);
35429                 return newValue;
35430             }
35431         }
35432         return value;
35433     },
35434
35435     filterValidation : function(e){
35436         if(!e.isNavKeyPress()){
35437             this.validationTask.delay(this.validationDelay);
35438         }
35439     },
35440
35441     // private
35442     onKeyUp : function(e){
35443         if(!e.isNavKeyPress()){
35444             this.autoSize();
35445         }
35446     },
35447
35448     /**
35449      * Resets the current field value to the originally-loaded value and clears any validation messages.
35450      * Also adds emptyText and emptyClass if the original value was blank.
35451      */
35452     reset : function(){
35453         Roo.form.TextField.superclass.reset.call(this);
35454         this.applyEmptyText();
35455     },
35456
35457     applyEmptyText : function(){
35458         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35459             this.setRawValue(this.emptyText);
35460             this.el.addClass(this.emptyClass);
35461         }
35462     },
35463
35464     // private
35465     preFocus : function(){
35466         if(this.emptyText){
35467             if(this.el.dom.value == this.emptyText){
35468                 this.setRawValue('');
35469             }
35470             this.el.removeClass(this.emptyClass);
35471         }
35472         if(this.selectOnFocus){
35473             this.el.dom.select();
35474         }
35475     },
35476
35477     // private
35478     postBlur : function(){
35479         this.applyEmptyText();
35480     },
35481
35482     // private
35483     filterKeys : function(e){
35484         var k = e.getKey();
35485         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35486             return;
35487         }
35488         var c = e.getCharCode(), cc = String.fromCharCode(c);
35489         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35490             return;
35491         }
35492         if(!this.maskRe.test(cc)){
35493             e.stopEvent();
35494         }
35495     },
35496
35497     setValue : function(v){
35498         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35499             this.el.removeClass(this.emptyClass);
35500         }
35501         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35502         this.applyEmptyText();
35503         this.autoSize();
35504     },
35505
35506     /**
35507      * Validates a value according to the field's validation rules and marks the field as invalid
35508      * if the validation fails
35509      * @param {Mixed} value The value to validate
35510      * @return {Boolean} True if the value is valid, else false
35511      */
35512     validateValue : function(value){
35513         if(value.length < 1 || value === this.emptyText){ // if it's blank
35514              if(this.allowBlank){
35515                 this.clearInvalid();
35516                 return true;
35517              }else{
35518                 this.markInvalid(this.blankText);
35519                 return false;
35520              }
35521         }
35522         if(value.length < this.minLength){
35523             this.markInvalid(String.format(this.minLengthText, this.minLength));
35524             return false;
35525         }
35526         if(value.length > this.maxLength){
35527             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35528             return false;
35529         }
35530         if(this.vtype){
35531             var vt = Roo.form.VTypes;
35532             if(!vt[this.vtype](value, this)){
35533                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35534                 return false;
35535             }
35536         }
35537         if(typeof this.validator == "function"){
35538             var msg = this.validator(value);
35539             if(msg !== true){
35540                 this.markInvalid(msg);
35541                 return false;
35542             }
35543         }
35544         if(this.regex && !this.regex.test(value)){
35545             this.markInvalid(this.regexText);
35546             return false;
35547         }
35548         return true;
35549     },
35550
35551     /**
35552      * Selects text in this field
35553      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35554      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35555      */
35556     selectText : function(start, end){
35557         var v = this.getRawValue();
35558         if(v.length > 0){
35559             start = start === undefined ? 0 : start;
35560             end = end === undefined ? v.length : end;
35561             var d = this.el.dom;
35562             if(d.setSelectionRange){
35563                 d.setSelectionRange(start, end);
35564             }else if(d.createTextRange){
35565                 var range = d.createTextRange();
35566                 range.moveStart("character", start);
35567                 range.moveEnd("character", v.length-end);
35568                 range.select();
35569             }
35570         }
35571     },
35572
35573     /**
35574      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35575      * This only takes effect if grow = true, and fires the autosize event.
35576      */
35577     autoSize : function(){
35578         if(!this.grow || !this.rendered){
35579             return;
35580         }
35581         if(!this.metrics){
35582             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35583         }
35584         var el = this.el;
35585         var v = el.dom.value;
35586         var d = document.createElement('div');
35587         d.appendChild(document.createTextNode(v));
35588         v = d.innerHTML;
35589         d = null;
35590         v += "&#160;";
35591         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35592         this.el.setWidth(w);
35593         this.fireEvent("autosize", this, w);
35594     }
35595 });/*
35596  * Based on:
35597  * Ext JS Library 1.1.1
35598  * Copyright(c) 2006-2007, Ext JS, LLC.
35599  *
35600  * Originally Released Under LGPL - original licence link has changed is not relivant.
35601  *
35602  * Fork - LGPL
35603  * <script type="text/javascript">
35604  */
35605  
35606 /**
35607  * @class Roo.form.Hidden
35608  * @extends Roo.form.TextField
35609  * Simple Hidden element used on forms 
35610  * 
35611  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35612  * 
35613  * @constructor
35614  * Creates a new Hidden form element.
35615  * @param {Object} config Configuration options
35616  */
35617
35618
35619
35620 // easy hidden field...
35621 Roo.form.Hidden = function(config){
35622     Roo.form.Hidden.superclass.constructor.call(this, config);
35623 };
35624   
35625 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35626     fieldLabel:      '',
35627     inputType:      'hidden',
35628     width:          50,
35629     allowBlank:     true,
35630     labelSeparator: '',
35631     hidden:         true,
35632     itemCls :       'x-form-item-display-none'
35633
35634
35635 });
35636
35637
35638 /*
35639  * Based on:
35640  * Ext JS Library 1.1.1
35641  * Copyright(c) 2006-2007, Ext JS, LLC.
35642  *
35643  * Originally Released Under LGPL - original licence link has changed is not relivant.
35644  *
35645  * Fork - LGPL
35646  * <script type="text/javascript">
35647  */
35648  
35649 /**
35650  * @class Roo.form.TriggerField
35651  * @extends Roo.form.TextField
35652  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35653  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35654  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35655  * for which you can provide a custom implementation.  For example:
35656  * <pre><code>
35657 var trigger = new Roo.form.TriggerField();
35658 trigger.onTriggerClick = myTriggerFn;
35659 trigger.applyTo('my-field');
35660 </code></pre>
35661  *
35662  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35663  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35664  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35665  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35666  * @constructor
35667  * Create a new TriggerField.
35668  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35669  * to the base TextField)
35670  */
35671 Roo.form.TriggerField = function(config){
35672     this.mimicing = false;
35673     Roo.form.TriggerField.superclass.constructor.call(this, config);
35674 };
35675
35676 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35677     /**
35678      * @cfg {String} triggerClass A CSS class to apply to the trigger
35679      */
35680     /**
35681      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35682      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35683      */
35684     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35685     /**
35686      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35687      */
35688     hideTrigger:false,
35689
35690     /** @cfg {Boolean} grow @hide */
35691     /** @cfg {Number} growMin @hide */
35692     /** @cfg {Number} growMax @hide */
35693
35694     /**
35695      * @hide 
35696      * @method
35697      */
35698     autoSize: Roo.emptyFn,
35699     // private
35700     monitorTab : true,
35701     // private
35702     deferHeight : true,
35703
35704     
35705     actionMode : 'wrap',
35706     // private
35707     onResize : function(w, h){
35708         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35709         if(typeof w == 'number'){
35710             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35711         }
35712     },
35713
35714     // private
35715     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35716
35717     // private
35718     getResizeEl : function(){
35719         return this.wrap;
35720     },
35721
35722     // private
35723     getPositionEl : function(){
35724         return this.wrap;
35725     },
35726
35727     // private
35728     alignErrorIcon : function(){
35729         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35730     },
35731
35732     // private
35733     onRender : function(ct, position){
35734         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35735         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35736         this.trigger = this.wrap.createChild(this.triggerConfig ||
35737                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35738         if(this.hideTrigger){
35739             this.trigger.setDisplayed(false);
35740         }
35741         this.initTrigger();
35742         if(!this.width){
35743             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35744         }
35745     },
35746
35747     // private
35748     initTrigger : function(){
35749         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35750         this.trigger.addClassOnOver('x-form-trigger-over');
35751         this.trigger.addClassOnClick('x-form-trigger-click');
35752     },
35753
35754     // private
35755     onDestroy : function(){
35756         if(this.trigger){
35757             this.trigger.removeAllListeners();
35758             this.trigger.remove();
35759         }
35760         if(this.wrap){
35761             this.wrap.remove();
35762         }
35763         Roo.form.TriggerField.superclass.onDestroy.call(this);
35764     },
35765
35766     // private
35767     onFocus : function(){
35768         Roo.form.TriggerField.superclass.onFocus.call(this);
35769         if(!this.mimicing){
35770             this.wrap.addClass('x-trigger-wrap-focus');
35771             this.mimicing = true;
35772             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35773             if(this.monitorTab){
35774                 this.el.on("keydown", this.checkTab, this);
35775             }
35776         }
35777     },
35778
35779     // private
35780     checkTab : function(e){
35781         if(e.getKey() == e.TAB){
35782             this.triggerBlur();
35783         }
35784     },
35785
35786     // private
35787     onBlur : function(){
35788         // do nothing
35789     },
35790
35791     // private
35792     mimicBlur : function(e, t){
35793         if(!this.wrap.contains(t) && this.validateBlur()){
35794             this.triggerBlur();
35795         }
35796     },
35797
35798     // private
35799     triggerBlur : function(){
35800         this.mimicing = false;
35801         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35802         if(this.monitorTab){
35803             this.el.un("keydown", this.checkTab, this);
35804         }
35805         this.wrap.removeClass('x-trigger-wrap-focus');
35806         Roo.form.TriggerField.superclass.onBlur.call(this);
35807     },
35808
35809     // private
35810     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35811     validateBlur : function(e, t){
35812         return true;
35813     },
35814
35815     // private
35816     onDisable : function(){
35817         Roo.form.TriggerField.superclass.onDisable.call(this);
35818         if(this.wrap){
35819             this.wrap.addClass('x-item-disabled');
35820         }
35821     },
35822
35823     // private
35824     onEnable : function(){
35825         Roo.form.TriggerField.superclass.onEnable.call(this);
35826         if(this.wrap){
35827             this.wrap.removeClass('x-item-disabled');
35828         }
35829     },
35830
35831     // private
35832     onShow : function(){
35833         var ae = this.getActionEl();
35834         
35835         if(ae){
35836             ae.dom.style.display = '';
35837             ae.dom.style.visibility = 'visible';
35838         }
35839     },
35840
35841     // private
35842     
35843     onHide : function(){
35844         var ae = this.getActionEl();
35845         ae.dom.style.display = 'none';
35846     },
35847
35848     /**
35849      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35850      * by an implementing function.
35851      * @method
35852      * @param {EventObject} e
35853      */
35854     onTriggerClick : Roo.emptyFn
35855 });
35856
35857 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35858 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35859 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35860 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35861     initComponent : function(){
35862         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35863
35864         this.triggerConfig = {
35865             tag:'span', cls:'x-form-twin-triggers', cn:[
35866             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35867             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35868         ]};
35869     },
35870
35871     getTrigger : function(index){
35872         return this.triggers[index];
35873     },
35874
35875     initTrigger : function(){
35876         var ts = this.trigger.select('.x-form-trigger', true);
35877         this.wrap.setStyle('overflow', 'hidden');
35878         var triggerField = this;
35879         ts.each(function(t, all, index){
35880             t.hide = function(){
35881                 var w = triggerField.wrap.getWidth();
35882                 this.dom.style.display = 'none';
35883                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35884             };
35885             t.show = function(){
35886                 var w = triggerField.wrap.getWidth();
35887                 this.dom.style.display = '';
35888                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35889             };
35890             var triggerIndex = 'Trigger'+(index+1);
35891
35892             if(this['hide'+triggerIndex]){
35893                 t.dom.style.display = 'none';
35894             }
35895             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
35896             t.addClassOnOver('x-form-trigger-over');
35897             t.addClassOnClick('x-form-trigger-click');
35898         }, this);
35899         this.triggers = ts.elements;
35900     },
35901
35902     onTrigger1Click : Roo.emptyFn,
35903     onTrigger2Click : Roo.emptyFn
35904 });/*
35905  * Based on:
35906  * Ext JS Library 1.1.1
35907  * Copyright(c) 2006-2007, Ext JS, LLC.
35908  *
35909  * Originally Released Under LGPL - original licence link has changed is not relivant.
35910  *
35911  * Fork - LGPL
35912  * <script type="text/javascript">
35913  */
35914  
35915 /**
35916  * @class Roo.form.TextArea
35917  * @extends Roo.form.TextField
35918  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
35919  * support for auto-sizing.
35920  * @constructor
35921  * Creates a new TextArea
35922  * @param {Object} config Configuration options
35923  */
35924 Roo.form.TextArea = function(config){
35925     Roo.form.TextArea.superclass.constructor.call(this, config);
35926     // these are provided exchanges for backwards compat
35927     // minHeight/maxHeight were replaced by growMin/growMax to be
35928     // compatible with TextField growing config values
35929     if(this.minHeight !== undefined){
35930         this.growMin = this.minHeight;
35931     }
35932     if(this.maxHeight !== undefined){
35933         this.growMax = this.maxHeight;
35934     }
35935 };
35936
35937 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
35938     /**
35939      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
35940      */
35941     growMin : 60,
35942     /**
35943      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
35944      */
35945     growMax: 1000,
35946     /**
35947      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
35948      * in the field (equivalent to setting overflow: hidden, defaults to false)
35949      */
35950     preventScrollbars: false,
35951     /**
35952      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35953      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
35954      */
35955
35956     // private
35957     onRender : function(ct, position){
35958         if(!this.el){
35959             this.defaultAutoCreate = {
35960                 tag: "textarea",
35961                 style:"width:300px;height:60px;",
35962                 autocomplete: "off"
35963             };
35964         }
35965         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
35966         if(this.grow){
35967             this.textSizeEl = Roo.DomHelper.append(document.body, {
35968                 tag: "pre", cls: "x-form-grow-sizer"
35969             });
35970             if(this.preventScrollbars){
35971                 this.el.setStyle("overflow", "hidden");
35972             }
35973             this.el.setHeight(this.growMin);
35974         }
35975     },
35976
35977     onDestroy : function(){
35978         if(this.textSizeEl){
35979             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
35980         }
35981         Roo.form.TextArea.superclass.onDestroy.call(this);
35982     },
35983
35984     // private
35985     onKeyUp : function(e){
35986         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
35987             this.autoSize();
35988         }
35989     },
35990
35991     /**
35992      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
35993      * This only takes effect if grow = true, and fires the autosize event if the height changes.
35994      */
35995     autoSize : function(){
35996         if(!this.grow || !this.textSizeEl){
35997             return;
35998         }
35999         var el = this.el;
36000         var v = el.dom.value;
36001         var ts = this.textSizeEl;
36002
36003         ts.innerHTML = '';
36004         ts.appendChild(document.createTextNode(v));
36005         v = ts.innerHTML;
36006
36007         Roo.fly(ts).setWidth(this.el.getWidth());
36008         if(v.length < 1){
36009             v = "&#160;&#160;";
36010         }else{
36011             if(Roo.isIE){
36012                 v = v.replace(/\n/g, '<p>&#160;</p>');
36013             }
36014             v += "&#160;\n&#160;";
36015         }
36016         ts.innerHTML = v;
36017         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36018         if(h != this.lastHeight){
36019             this.lastHeight = h;
36020             this.el.setHeight(h);
36021             this.fireEvent("autosize", this, h);
36022         }
36023     }
36024 });/*
36025  * Based on:
36026  * Ext JS Library 1.1.1
36027  * Copyright(c) 2006-2007, Ext JS, LLC.
36028  *
36029  * Originally Released Under LGPL - original licence link has changed is not relivant.
36030  *
36031  * Fork - LGPL
36032  * <script type="text/javascript">
36033  */
36034  
36035
36036 /**
36037  * @class Roo.form.NumberField
36038  * @extends Roo.form.TextField
36039  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36040  * @constructor
36041  * Creates a new NumberField
36042  * @param {Object} config Configuration options
36043  */
36044 Roo.form.NumberField = function(config){
36045     Roo.form.NumberField.superclass.constructor.call(this, config);
36046 };
36047
36048 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36049     /**
36050      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36051      */
36052     fieldClass: "x-form-field x-form-num-field",
36053     /**
36054      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36055      */
36056     allowDecimals : true,
36057     /**
36058      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36059      */
36060     decimalSeparator : ".",
36061     /**
36062      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36063      */
36064     decimalPrecision : 2,
36065     /**
36066      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36067      */
36068     allowNegative : true,
36069     /**
36070      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36071      */
36072     minValue : Number.NEGATIVE_INFINITY,
36073     /**
36074      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36075      */
36076     maxValue : Number.MAX_VALUE,
36077     /**
36078      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36079      */
36080     minText : "The minimum value for this field is {0}",
36081     /**
36082      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36083      */
36084     maxText : "The maximum value for this field is {0}",
36085     /**
36086      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36087      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36088      */
36089     nanText : "{0} is not a valid number",
36090
36091     // private
36092     initEvents : function(){
36093         Roo.form.NumberField.superclass.initEvents.call(this);
36094         var allowed = "0123456789";
36095         if(this.allowDecimals){
36096             allowed += this.decimalSeparator;
36097         }
36098         if(this.allowNegative){
36099             allowed += "-";
36100         }
36101         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36102         var keyPress = function(e){
36103             var k = e.getKey();
36104             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36105                 return;
36106             }
36107             var c = e.getCharCode();
36108             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36109                 e.stopEvent();
36110             }
36111         };
36112         this.el.on("keypress", keyPress, this);
36113     },
36114
36115     // private
36116     validateValue : function(value){
36117         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36118             return false;
36119         }
36120         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36121              return true;
36122         }
36123         var num = this.parseValue(value);
36124         if(isNaN(num)){
36125             this.markInvalid(String.format(this.nanText, value));
36126             return false;
36127         }
36128         if(num < this.minValue){
36129             this.markInvalid(String.format(this.minText, this.minValue));
36130             return false;
36131         }
36132         if(num > this.maxValue){
36133             this.markInvalid(String.format(this.maxText, this.maxValue));
36134             return false;
36135         }
36136         return true;
36137     },
36138
36139     getValue : function(){
36140         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36141     },
36142
36143     // private
36144     parseValue : function(value){
36145         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36146         return isNaN(value) ? '' : value;
36147     },
36148
36149     // private
36150     fixPrecision : function(value){
36151         var nan = isNaN(value);
36152         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36153             return nan ? '' : value;
36154         }
36155         return parseFloat(value).toFixed(this.decimalPrecision);
36156     },
36157
36158     setValue : function(v){
36159         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36160     },
36161
36162     // private
36163     decimalPrecisionFcn : function(v){
36164         return Math.floor(v);
36165     },
36166
36167     beforeBlur : function(){
36168         var v = this.parseValue(this.getRawValue());
36169         if(v){
36170             this.setValue(this.fixPrecision(v));
36171         }
36172     }
36173 });/*
36174  * Based on:
36175  * Ext JS Library 1.1.1
36176  * Copyright(c) 2006-2007, Ext JS, LLC.
36177  *
36178  * Originally Released Under LGPL - original licence link has changed is not relivant.
36179  *
36180  * Fork - LGPL
36181  * <script type="text/javascript">
36182  */
36183  
36184 /**
36185  * @class Roo.form.DateField
36186  * @extends Roo.form.TriggerField
36187  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36188 * @constructor
36189 * Create a new DateField
36190 * @param {Object} config
36191  */
36192 Roo.form.DateField = function(config){
36193     Roo.form.DateField.superclass.constructor.call(this, config);
36194     
36195       this.addEvents({
36196          
36197         /**
36198          * @event select
36199          * Fires when a date is selected
36200              * @param {Roo.form.DateField} combo This combo box
36201              * @param {Date} date The date selected
36202              */
36203         'select' : true
36204          
36205     });
36206     
36207     
36208     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36209     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36210     this.ddMatch = null;
36211     if(this.disabledDates){
36212         var dd = this.disabledDates;
36213         var re = "(?:";
36214         for(var i = 0; i < dd.length; i++){
36215             re += dd[i];
36216             if(i != dd.length-1) re += "|";
36217         }
36218         this.ddMatch = new RegExp(re + ")");
36219     }
36220 };
36221
36222 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36223     /**
36224      * @cfg {String} format
36225      * The default date format string which can be overriden for localization support.  The format must be
36226      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36227      */
36228     format : "m/d/y",
36229     /**
36230      * @cfg {String} altFormats
36231      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36232      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36233      */
36234     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36235     /**
36236      * @cfg {Array} disabledDays
36237      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36238      */
36239     disabledDays : null,
36240     /**
36241      * @cfg {String} disabledDaysText
36242      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36243      */
36244     disabledDaysText : "Disabled",
36245     /**
36246      * @cfg {Array} disabledDates
36247      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36248      * expression so they are very powerful. Some examples:
36249      * <ul>
36250      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36251      * <li>["03/08", "09/16"] would disable those days for every year</li>
36252      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36253      * <li>["03/../2006"] would disable every day in March 2006</li>
36254      * <li>["^03"] would disable every day in every March</li>
36255      * </ul>
36256      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36257      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36258      */
36259     disabledDates : null,
36260     /**
36261      * @cfg {String} disabledDatesText
36262      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36263      */
36264     disabledDatesText : "Disabled",
36265     /**
36266      * @cfg {Date/String} minValue
36267      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36268      * valid format (defaults to null).
36269      */
36270     minValue : null,
36271     /**
36272      * @cfg {Date/String} maxValue
36273      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36274      * valid format (defaults to null).
36275      */
36276     maxValue : null,
36277     /**
36278      * @cfg {String} minText
36279      * The error text to display when the date in the cell is before minValue (defaults to
36280      * 'The date in this field must be after {minValue}').
36281      */
36282     minText : "The date in this field must be equal to or after {0}",
36283     /**
36284      * @cfg {String} maxText
36285      * The error text to display when the date in the cell is after maxValue (defaults to
36286      * 'The date in this field must be before {maxValue}').
36287      */
36288     maxText : "The date in this field must be equal to or before {0}",
36289     /**
36290      * @cfg {String} invalidText
36291      * The error text to display when the date in the field is invalid (defaults to
36292      * '{value} is not a valid date - it must be in the format {format}').
36293      */
36294     invalidText : "{0} is not a valid date - it must be in the format {1}",
36295     /**
36296      * @cfg {String} triggerClass
36297      * An additional CSS class used to style the trigger button.  The trigger will always get the
36298      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36299      * which displays a calendar icon).
36300      */
36301     triggerClass : 'x-form-date-trigger',
36302     
36303
36304     /**
36305      * @cfg {bool} useIso
36306      * if enabled, then the date field will use a hidden field to store the 
36307      * real value as iso formated date. default (false)
36308      */ 
36309     useIso : false,
36310     /**
36311      * @cfg {String/Object} autoCreate
36312      * A DomHelper element spec, or true for a default element spec (defaults to
36313      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36314      */ 
36315     // private
36316     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36317     
36318     // private
36319     hiddenField: false,
36320     
36321     onRender : function(ct, position)
36322     {
36323         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36324         if (this.useIso) {
36325             this.el.dom.removeAttribute('name'); 
36326             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36327                     'before', true);
36328             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36329             // prevent input submission
36330             this.hiddenName = this.name;
36331         }
36332             
36333             
36334     },
36335     
36336     // private
36337     validateValue : function(value)
36338     {
36339         value = this.formatDate(value);
36340         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36341             return false;
36342         }
36343         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36344              return true;
36345         }
36346         var svalue = value;
36347         value = this.parseDate(value);
36348         if(!value){
36349             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36350             return false;
36351         }
36352         var time = value.getTime();
36353         if(this.minValue && time < this.minValue.getTime()){
36354             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36355             return false;
36356         }
36357         if(this.maxValue && time > this.maxValue.getTime()){
36358             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36359             return false;
36360         }
36361         if(this.disabledDays){
36362             var day = value.getDay();
36363             for(var i = 0; i < this.disabledDays.length; i++) {
36364                 if(day === this.disabledDays[i]){
36365                     this.markInvalid(this.disabledDaysText);
36366                     return false;
36367                 }
36368             }
36369         }
36370         var fvalue = this.formatDate(value);
36371         if(this.ddMatch && this.ddMatch.test(fvalue)){
36372             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36373             return false;
36374         }
36375         return true;
36376     },
36377
36378     // private
36379     // Provides logic to override the default TriggerField.validateBlur which just returns true
36380     validateBlur : function(){
36381         return !this.menu || !this.menu.isVisible();
36382     },
36383
36384     /**
36385      * Returns the current date value of the date field.
36386      * @return {Date} The date value
36387      */
36388     getValue : function(){
36389         
36390         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36391     },
36392
36393     /**
36394      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36395      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36396      * (the default format used is "m/d/y").
36397      * <br />Usage:
36398      * <pre><code>
36399 //All of these calls set the same date value (May 4, 2006)
36400
36401 //Pass a date object:
36402 var dt = new Date('5/4/06');
36403 dateField.setValue(dt);
36404
36405 //Pass a date string (default format):
36406 dateField.setValue('5/4/06');
36407
36408 //Pass a date string (custom format):
36409 dateField.format = 'Y-m-d';
36410 dateField.setValue('2006-5-4');
36411 </code></pre>
36412      * @param {String/Date} date The date or valid date string
36413      */
36414     setValue : function(date){
36415         if (this.hiddenField) {
36416             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36417         }
36418         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36419     },
36420
36421     // private
36422     parseDate : function(value){
36423         if(!value || value instanceof Date){
36424             return value;
36425         }
36426         var v = Date.parseDate(value, this.format);
36427         if(!v && this.altFormats){
36428             if(!this.altFormatsArray){
36429                 this.altFormatsArray = this.altFormats.split("|");
36430             }
36431             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36432                 v = Date.parseDate(value, this.altFormatsArray[i]);
36433             }
36434         }
36435         return v;
36436     },
36437
36438     // private
36439     formatDate : function(date, fmt){
36440         return (!date || !(date instanceof Date)) ?
36441                date : date.dateFormat(fmt || this.format);
36442     },
36443
36444     // private
36445     menuListeners : {
36446         select: function(m, d){
36447             this.setValue(d);
36448             this.fireEvent('select', this, d);
36449         },
36450         show : function(){ // retain focus styling
36451             this.onFocus();
36452         },
36453         hide : function(){
36454             this.focus.defer(10, this);
36455             var ml = this.menuListeners;
36456             this.menu.un("select", ml.select,  this);
36457             this.menu.un("show", ml.show,  this);
36458             this.menu.un("hide", ml.hide,  this);
36459         }
36460     },
36461
36462     // private
36463     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36464     onTriggerClick : function(){
36465         if(this.disabled){
36466             return;
36467         }
36468         if(this.menu == null){
36469             this.menu = new Roo.menu.DateMenu();
36470         }
36471         Roo.apply(this.menu.picker,  {
36472             showClear: this.allowBlank,
36473             minDate : this.minValue,
36474             maxDate : this.maxValue,
36475             disabledDatesRE : this.ddMatch,
36476             disabledDatesText : this.disabledDatesText,
36477             disabledDays : this.disabledDays,
36478             disabledDaysText : this.disabledDaysText,
36479             format : this.format,
36480             minText : String.format(this.minText, this.formatDate(this.minValue)),
36481             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36482         });
36483         this.menu.on(Roo.apply({}, this.menuListeners, {
36484             scope:this
36485         }));
36486         this.menu.picker.setValue(this.getValue() || new Date());
36487         this.menu.show(this.el, "tl-bl?");
36488     },
36489
36490     beforeBlur : function(){
36491         var v = this.parseDate(this.getRawValue());
36492         if(v){
36493             this.setValue(v);
36494         }
36495     }
36496
36497     /** @cfg {Boolean} grow @hide */
36498     /** @cfg {Number} growMin @hide */
36499     /** @cfg {Number} growMax @hide */
36500     /**
36501      * @hide
36502      * @method autoSize
36503      */
36504 });/*
36505  * Based on:
36506  * Ext JS Library 1.1.1
36507  * Copyright(c) 2006-2007, Ext JS, LLC.
36508  *
36509  * Originally Released Under LGPL - original licence link has changed is not relivant.
36510  *
36511  * Fork - LGPL
36512  * <script type="text/javascript">
36513  */
36514  
36515
36516 /**
36517  * @class Roo.form.ComboBox
36518  * @extends Roo.form.TriggerField
36519  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36520  * @constructor
36521  * Create a new ComboBox.
36522  * @param {Object} config Configuration options
36523  */
36524 Roo.form.ComboBox = function(config){
36525     Roo.form.ComboBox.superclass.constructor.call(this, config);
36526     this.addEvents({
36527         /**
36528          * @event expand
36529          * Fires when the dropdown list is expanded
36530              * @param {Roo.form.ComboBox} combo This combo box
36531              */
36532         'expand' : true,
36533         /**
36534          * @event collapse
36535          * Fires when the dropdown list is collapsed
36536              * @param {Roo.form.ComboBox} combo This combo box
36537              */
36538         'collapse' : true,
36539         /**
36540          * @event beforeselect
36541          * Fires before a list item is selected. Return false to cancel the selection.
36542              * @param {Roo.form.ComboBox} combo This combo box
36543              * @param {Roo.data.Record} record The data record returned from the underlying store
36544              * @param {Number} index The index of the selected item in the dropdown list
36545              */
36546         'beforeselect' : true,
36547         /**
36548          * @event select
36549          * Fires when a list item is selected
36550              * @param {Roo.form.ComboBox} combo This combo box
36551              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36552              * @param {Number} index The index of the selected item in the dropdown list
36553              */
36554         'select' : true,
36555         /**
36556          * @event beforequery
36557          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36558          * The event object passed has these properties:
36559              * @param {Roo.form.ComboBox} combo This combo box
36560              * @param {String} query The query
36561              * @param {Boolean} forceAll true to force "all" query
36562              * @param {Boolean} cancel true to cancel the query
36563              * @param {Object} e The query event object
36564              */
36565         'beforequery': true
36566     });
36567     if(this.transform){
36568         this.allowDomMove = false;
36569         var s = Roo.getDom(this.transform);
36570         if(!this.hiddenName){
36571             this.hiddenName = s.name;
36572         }
36573         if(!this.store){
36574             this.mode = 'local';
36575             var d = [], opts = s.options;
36576             for(var i = 0, len = opts.length;i < len; i++){
36577                 var o = opts[i];
36578                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36579                 if(o.selected) {
36580                     this.value = value;
36581                 }
36582                 d.push([value, o.text]);
36583             }
36584             this.store = new Roo.data.SimpleStore({
36585                 'id': 0,
36586                 fields: ['value', 'text'],
36587                 data : d
36588             });
36589             this.valueField = 'value';
36590             this.displayField = 'text';
36591         }
36592         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36593         if(!this.lazyRender){
36594             this.target = true;
36595             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36596             s.parentNode.removeChild(s); // remove it
36597             this.render(this.el.parentNode);
36598         }else{
36599             s.parentNode.removeChild(s); // remove it
36600         }
36601
36602     }
36603     if (this.store) {
36604         this.store = Roo.factory(this.store, Roo.data);
36605     }
36606     
36607     this.selectedIndex = -1;
36608     if(this.mode == 'local'){
36609         if(config.queryDelay === undefined){
36610             this.queryDelay = 10;
36611         }
36612         if(config.minChars === undefined){
36613             this.minChars = 0;
36614         }
36615     }
36616 };
36617
36618 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36619     /**
36620      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36621      */
36622     /**
36623      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36624      * rendering into an Roo.Editor, defaults to false)
36625      */
36626     /**
36627      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36628      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36629      */
36630     /**
36631      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36632      */
36633     /**
36634      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36635      * the dropdown list (defaults to undefined, with no header element)
36636      */
36637
36638      /**
36639      * @cfg {String/Roo.Template} tpl The template to use to render the output
36640      */
36641      
36642     // private
36643     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36644     /**
36645      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36646      */
36647     listWidth: undefined,
36648     /**
36649      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36650      * mode = 'remote' or 'text' if mode = 'local')
36651      */
36652     displayField: undefined,
36653     /**
36654      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36655      * mode = 'remote' or 'value' if mode = 'local'). 
36656      * Note: use of a valueField requires the user make a selection
36657      * in order for a value to be mapped.
36658      */
36659     valueField: undefined,
36660     /**
36661      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36662      * field's data value (defaults to the underlying DOM element's name)
36663      */
36664     hiddenName: undefined,
36665     /**
36666      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36667      */
36668     listClass: '',
36669     /**
36670      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36671      */
36672     selectedClass: 'x-combo-selected',
36673     /**
36674      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36675      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36676      * which displays a downward arrow icon).
36677      */
36678     triggerClass : 'x-form-arrow-trigger',
36679     /**
36680      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36681      */
36682     shadow:'sides',
36683     /**
36684      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36685      * anchor positions (defaults to 'tl-bl')
36686      */
36687     listAlign: 'tl-bl?',
36688     /**
36689      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36690      */
36691     maxHeight: 300,
36692     /**
36693      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36694      * query specified by the allQuery config option (defaults to 'query')
36695      */
36696     triggerAction: 'query',
36697     /**
36698      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36699      * (defaults to 4, does not apply if editable = false)
36700      */
36701     minChars : 4,
36702     /**
36703      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36704      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36705      */
36706     typeAhead: false,
36707     /**
36708      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36709      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36710      */
36711     queryDelay: 500,
36712     /**
36713      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36714      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36715      */
36716     pageSize: 0,
36717     /**
36718      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36719      * when editable = true (defaults to false)
36720      */
36721     selectOnFocus:false,
36722     /**
36723      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36724      */
36725     queryParam: 'query',
36726     /**
36727      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36728      * when mode = 'remote' (defaults to 'Loading...')
36729      */
36730     loadingText: 'Loading...',
36731     /**
36732      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36733      */
36734     resizable: false,
36735     /**
36736      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36737      */
36738     handleHeight : 8,
36739     /**
36740      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36741      * traditional select (defaults to true)
36742      */
36743     editable: true,
36744     /**
36745      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36746      */
36747     allQuery: '',
36748     /**
36749      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36750      */
36751     mode: 'remote',
36752     /**
36753      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36754      * listWidth has a higher value)
36755      */
36756     minListWidth : 70,
36757     /**
36758      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36759      * allow the user to set arbitrary text into the field (defaults to false)
36760      */
36761     forceSelection:false,
36762     /**
36763      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36764      * if typeAhead = true (defaults to 250)
36765      */
36766     typeAheadDelay : 250,
36767     /**
36768      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36769      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36770      */
36771     valueNotFoundText : undefined,
36772     /**
36773      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36774      */
36775     blockFocus : false,
36776     
36777     /**
36778      * @cfg {bool} disableClear Disable showing of clear button.
36779      */
36780     disableClear : false,
36781     
36782     // private
36783     onRender : function(ct, position){
36784         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36785         if(this.hiddenName){
36786             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36787                     'before', true);
36788             this.hiddenField.value =
36789                 this.hiddenValue !== undefined ? this.hiddenValue :
36790                 this.value !== undefined ? this.value : '';
36791
36792             // prevent input submission
36793             this.el.dom.removeAttribute('name');
36794         }
36795         if(Roo.isGecko){
36796             this.el.dom.setAttribute('autocomplete', 'off');
36797         }
36798
36799         var cls = 'x-combo-list';
36800
36801         this.list = new Roo.Layer({
36802             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36803         });
36804
36805         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36806         this.list.setWidth(lw);
36807         this.list.swallowEvent('mousewheel');
36808         this.assetHeight = 0;
36809
36810         if(this.title){
36811             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36812             this.assetHeight += this.header.getHeight();
36813         }
36814
36815         this.innerList = this.list.createChild({cls:cls+'-inner'});
36816         this.innerList.on('mouseover', this.onViewOver, this);
36817         this.innerList.on('mousemove', this.onViewMove, this);
36818         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36819         
36820         if(this.allowBlank && !this.pageSize && !this.disableClear){
36821             this.footer = this.list.createChild({cls:cls+'-ft'});
36822             this.pageTb = new Roo.Toolbar(this.footer);
36823            
36824         }
36825         if(this.pageSize){
36826             this.footer = this.list.createChild({cls:cls+'-ft'});
36827             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36828                     {pageSize: this.pageSize});
36829             
36830         }
36831         
36832         if (this.pageTb && this.allowBlank && !this.disableClear) {
36833             var _this = this;
36834             this.pageTb.add(new Roo.Toolbar.Fill(), {
36835                 cls: 'x-btn-icon x-btn-clear',
36836                 text: '&#160;',
36837                 handler: function()
36838                 {
36839                     _this.collapse();
36840                     _this.clearValue();
36841                     _this.onSelect(false, -1);
36842                 }
36843             });
36844         }
36845         if (this.footer) {
36846             this.assetHeight += this.footer.getHeight();
36847         }
36848         
36849
36850         if(!this.tpl){
36851             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36852         }
36853
36854         this.view = new Roo.View(this.innerList, this.tpl, {
36855             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36856         });
36857
36858         this.view.on('click', this.onViewClick, this);
36859
36860         this.store.on('beforeload', this.onBeforeLoad, this);
36861         this.store.on('load', this.onLoad, this);
36862         this.store.on('loadexception', this.collapse, this);
36863
36864         if(this.resizable){
36865             this.resizer = new Roo.Resizable(this.list,  {
36866                pinned:true, handles:'se'
36867             });
36868             this.resizer.on('resize', function(r, w, h){
36869                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36870                 this.listWidth = w;
36871                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36872                 this.restrictHeight();
36873             }, this);
36874             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36875         }
36876         if(!this.editable){
36877             this.editable = true;
36878             this.setEditable(false);
36879         }
36880     },
36881
36882     // private
36883     initEvents : function(){
36884         Roo.form.ComboBox.superclass.initEvents.call(this);
36885
36886         this.keyNav = new Roo.KeyNav(this.el, {
36887             "up" : function(e){
36888                 this.inKeyMode = true;
36889                 this.selectPrev();
36890             },
36891
36892             "down" : function(e){
36893                 if(!this.isExpanded()){
36894                     this.onTriggerClick();
36895                 }else{
36896                     this.inKeyMode = true;
36897                     this.selectNext();
36898                 }
36899             },
36900
36901             "enter" : function(e){
36902                 this.onViewClick();
36903                 //return true;
36904             },
36905
36906             "esc" : function(e){
36907                 this.collapse();
36908             },
36909
36910             "tab" : function(e){
36911                 this.onViewClick(false);
36912                 return true;
36913             },
36914
36915             scope : this,
36916
36917             doRelay : function(foo, bar, hname){
36918                 if(hname == 'down' || this.scope.isExpanded()){
36919                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
36920                 }
36921                 return true;
36922             },
36923
36924             forceKeyDown: true
36925         });
36926         this.queryDelay = Math.max(this.queryDelay || 10,
36927                 this.mode == 'local' ? 10 : 250);
36928         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
36929         if(this.typeAhead){
36930             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
36931         }
36932         if(this.editable !== false){
36933             this.el.on("keyup", this.onKeyUp, this);
36934         }
36935         if(this.forceSelection){
36936             this.on('blur', this.doForce, this);
36937         }
36938     },
36939
36940     onDestroy : function(){
36941         if(this.view){
36942             this.view.setStore(null);
36943             this.view.el.removeAllListeners();
36944             this.view.el.remove();
36945             this.view.purgeListeners();
36946         }
36947         if(this.list){
36948             this.list.destroy();
36949         }
36950         if(this.store){
36951             this.store.un('beforeload', this.onBeforeLoad, this);
36952             this.store.un('load', this.onLoad, this);
36953             this.store.un('loadexception', this.collapse, this);
36954         }
36955         Roo.form.ComboBox.superclass.onDestroy.call(this);
36956     },
36957
36958     // private
36959     fireKey : function(e){
36960         if(e.isNavKeyPress() && !this.list.isVisible()){
36961             this.fireEvent("specialkey", this, e);
36962         }
36963     },
36964
36965     // private
36966     onResize: function(w, h){
36967         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
36968         if(this.list && this.listWidth === undefined){
36969             var lw = Math.max(w, this.minListWidth);
36970             this.list.setWidth(lw);
36971             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36972         }
36973     },
36974
36975     /**
36976      * Allow or prevent the user from directly editing the field text.  If false is passed,
36977      * the user will only be able to select from the items defined in the dropdown list.  This method
36978      * is the runtime equivalent of setting the 'editable' config option at config time.
36979      * @param {Boolean} value True to allow the user to directly edit the field text
36980      */
36981     setEditable : function(value){
36982         if(value == this.editable){
36983             return;
36984         }
36985         this.editable = value;
36986         if(!value){
36987             this.el.dom.setAttribute('readOnly', true);
36988             this.el.on('mousedown', this.onTriggerClick,  this);
36989             this.el.addClass('x-combo-noedit');
36990         }else{
36991             this.el.dom.setAttribute('readOnly', false);
36992             this.el.un('mousedown', this.onTriggerClick,  this);
36993             this.el.removeClass('x-combo-noedit');
36994         }
36995     },
36996
36997     // private
36998     onBeforeLoad : function(){
36999         if(!this.hasFocus){
37000             return;
37001         }
37002         this.innerList.update(this.loadingText ?
37003                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37004         this.restrictHeight();
37005         this.selectedIndex = -1;
37006     },
37007
37008     // private
37009     onLoad : function(){
37010         if(!this.hasFocus){
37011             return;
37012         }
37013         if(this.store.getCount() > 0){
37014             this.expand();
37015             this.restrictHeight();
37016             if(this.lastQuery == this.allQuery){
37017                 if(this.editable){
37018                     this.el.dom.select();
37019                 }
37020                 if(!this.selectByValue(this.value, true)){
37021                     this.select(0, true);
37022                 }
37023             }else{
37024                 this.selectNext();
37025                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37026                     this.taTask.delay(this.typeAheadDelay);
37027                 }
37028             }
37029         }else{
37030             this.onEmptyResults();
37031         }
37032         //this.el.focus();
37033     },
37034
37035     // private
37036     onTypeAhead : function(){
37037         if(this.store.getCount() > 0){
37038             var r = this.store.getAt(0);
37039             var newValue = r.data[this.displayField];
37040             var len = newValue.length;
37041             var selStart = this.getRawValue().length;
37042             if(selStart != len){
37043                 this.setRawValue(newValue);
37044                 this.selectText(selStart, newValue.length);
37045             }
37046         }
37047     },
37048
37049     // private
37050     onSelect : function(record, index){
37051         if(this.fireEvent('beforeselect', this, record, index) !== false){
37052             this.setFromData(index > -1 ? record.data : false);
37053             this.collapse();
37054             this.fireEvent('select', this, record, index);
37055         }
37056     },
37057
37058     /**
37059      * Returns the currently selected field value or empty string if no value is set.
37060      * @return {String} value The selected value
37061      */
37062     getValue : function(){
37063         if(this.valueField){
37064             return typeof this.value != 'undefined' ? this.value : '';
37065         }else{
37066             return Roo.form.ComboBox.superclass.getValue.call(this);
37067         }
37068     },
37069
37070     /**
37071      * Clears any text/value currently set in the field
37072      */
37073     clearValue : function(){
37074         if(this.hiddenField){
37075             this.hiddenField.value = '';
37076         }
37077         this.value = '';
37078         this.setRawValue('');
37079         this.lastSelectionText = '';
37080         this.applyEmptyText();
37081     },
37082
37083     /**
37084      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37085      * will be displayed in the field.  If the value does not match the data value of an existing item,
37086      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37087      * Otherwise the field will be blank (although the value will still be set).
37088      * @param {String} value The value to match
37089      */
37090     setValue : function(v){
37091         var text = v;
37092         if(this.valueField){
37093             var r = this.findRecord(this.valueField, v);
37094             if(r){
37095                 text = r.data[this.displayField];
37096             }else if(this.valueNotFoundText !== undefined){
37097                 text = this.valueNotFoundText;
37098             }
37099         }
37100         this.lastSelectionText = text;
37101         if(this.hiddenField){
37102             this.hiddenField.value = v;
37103         }
37104         Roo.form.ComboBox.superclass.setValue.call(this, text);
37105         this.value = v;
37106     },
37107     /**
37108      * @property {Object} the last set data for the element
37109      */
37110     
37111     lastData : false,
37112     /**
37113      * Sets the value of the field based on a object which is related to the record format for the store.
37114      * @param {Object} value the value to set as. or false on reset?
37115      */
37116     setFromData : function(o){
37117         var dv = ''; // display value
37118         var vv = ''; // value value..
37119         this.lastData = o;
37120         if (this.displayField) {
37121             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37122         } else {
37123             // this is an error condition!!!
37124             console.log('no value field set for '+ this.name);
37125         }
37126         
37127         if(this.valueField){
37128             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37129         }
37130         if(this.hiddenField){
37131             this.hiddenField.value = vv;
37132             
37133             this.lastSelectionText = dv;
37134             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37135             this.value = vv;
37136             return;
37137         }
37138         // no hidden field.. - we store the value in 'value', but still display
37139         // display field!!!!
37140         this.lastSelectionText = dv;
37141         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37142         this.value = vv;
37143         
37144         
37145     },
37146     // private
37147     findRecord : function(prop, value){
37148         var record;
37149         if(this.store.getCount() > 0){
37150             this.store.each(function(r){
37151                 if(r.data[prop] == value){
37152                     record = r;
37153                     return false;
37154                 }
37155             });
37156         }
37157         return record;
37158     },
37159
37160     // private
37161     onViewMove : function(e, t){
37162         this.inKeyMode = false;
37163     },
37164
37165     // private
37166     onViewOver : function(e, t){
37167         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37168             return;
37169         }
37170         var item = this.view.findItemFromChild(t);
37171         if(item){
37172             var index = this.view.indexOf(item);
37173             this.select(index, false);
37174         }
37175     },
37176
37177     // private
37178     onViewClick : function(doFocus){
37179         var index = this.view.getSelectedIndexes()[0];
37180         var r = this.store.getAt(index);
37181         if(r){
37182             this.onSelect(r, index);
37183         }
37184         if(doFocus !== false && !this.blockFocus){
37185             this.el.focus();
37186         }
37187     },
37188
37189     // private
37190     restrictHeight : function(){
37191         this.innerList.dom.style.height = '';
37192         var inner = this.innerList.dom;
37193         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37194         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37195         this.list.beginUpdate();
37196         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37197         this.list.alignTo(this.el, this.listAlign);
37198         this.list.endUpdate();
37199     },
37200
37201     // private
37202     onEmptyResults : function(){
37203         this.collapse();
37204     },
37205
37206     /**
37207      * Returns true if the dropdown list is expanded, else false.
37208      */
37209     isExpanded : function(){
37210         return this.list.isVisible();
37211     },
37212
37213     /**
37214      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37215      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37216      * @param {String} value The data value of the item to select
37217      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37218      * selected item if it is not currently in view (defaults to true)
37219      * @return {Boolean} True if the value matched an item in the list, else false
37220      */
37221     selectByValue : function(v, scrollIntoView){
37222         if(v !== undefined && v !== null){
37223             var r = this.findRecord(this.valueField || this.displayField, v);
37224             if(r){
37225                 this.select(this.store.indexOf(r), scrollIntoView);
37226                 return true;
37227             }
37228         }
37229         return false;
37230     },
37231
37232     /**
37233      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37234      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37235      * @param {Number} index The zero-based index of the list item to select
37236      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37237      * selected item if it is not currently in view (defaults to true)
37238      */
37239     select : function(index, scrollIntoView){
37240         this.selectedIndex = index;
37241         this.view.select(index);
37242         if(scrollIntoView !== false){
37243             var el = this.view.getNode(index);
37244             if(el){
37245                 this.innerList.scrollChildIntoView(el, false);
37246             }
37247         }
37248     },
37249
37250     // private
37251     selectNext : function(){
37252         var ct = this.store.getCount();
37253         if(ct > 0){
37254             if(this.selectedIndex == -1){
37255                 this.select(0);
37256             }else if(this.selectedIndex < ct-1){
37257                 this.select(this.selectedIndex+1);
37258             }
37259         }
37260     },
37261
37262     // private
37263     selectPrev : function(){
37264         var ct = this.store.getCount();
37265         if(ct > 0){
37266             if(this.selectedIndex == -1){
37267                 this.select(0);
37268             }else if(this.selectedIndex != 0){
37269                 this.select(this.selectedIndex-1);
37270             }
37271         }
37272     },
37273
37274     // private
37275     onKeyUp : function(e){
37276         if(this.editable !== false && !e.isSpecialKey()){
37277             this.lastKey = e.getKey();
37278             this.dqTask.delay(this.queryDelay);
37279         }
37280     },
37281
37282     // private
37283     validateBlur : function(){
37284         return !this.list || !this.list.isVisible();   
37285     },
37286
37287     // private
37288     initQuery : function(){
37289         this.doQuery(this.getRawValue());
37290     },
37291
37292     // private
37293     doForce : function(){
37294         if(this.el.dom.value.length > 0){
37295             this.el.dom.value =
37296                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37297             this.applyEmptyText();
37298         }
37299     },
37300
37301     /**
37302      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37303      * query allowing the query action to be canceled if needed.
37304      * @param {String} query The SQL query to execute
37305      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37306      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37307      * saved in the current store (defaults to false)
37308      */
37309     doQuery : function(q, forceAll){
37310         if(q === undefined || q === null){
37311             q = '';
37312         }
37313         var qe = {
37314             query: q,
37315             forceAll: forceAll,
37316             combo: this,
37317             cancel:false
37318         };
37319         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37320             return false;
37321         }
37322         q = qe.query;
37323         forceAll = qe.forceAll;
37324         if(forceAll === true || (q.length >= this.minChars)){
37325             if(this.lastQuery != q){
37326                 this.lastQuery = q;
37327                 if(this.mode == 'local'){
37328                     this.selectedIndex = -1;
37329                     if(forceAll){
37330                         this.store.clearFilter();
37331                     }else{
37332                         this.store.filter(this.displayField, q);
37333                     }
37334                     this.onLoad();
37335                 }else{
37336                     this.store.baseParams[this.queryParam] = q;
37337                     this.store.load({
37338                         params: this.getParams(q)
37339                     });
37340                     this.expand();
37341                 }
37342             }else{
37343                 this.selectedIndex = -1;
37344                 this.onLoad();   
37345             }
37346         }
37347     },
37348
37349     // private
37350     getParams : function(q){
37351         var p = {};
37352         //p[this.queryParam] = q;
37353         if(this.pageSize){
37354             p.start = 0;
37355             p.limit = this.pageSize;
37356         }
37357         return p;
37358     },
37359
37360     /**
37361      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37362      */
37363     collapse : function(){
37364         if(!this.isExpanded()){
37365             return;
37366         }
37367         this.list.hide();
37368         Roo.get(document).un('mousedown', this.collapseIf, this);
37369         Roo.get(document).un('mousewheel', this.collapseIf, this);
37370         this.fireEvent('collapse', this);
37371     },
37372
37373     // private
37374     collapseIf : function(e){
37375         if(!e.within(this.wrap) && !e.within(this.list)){
37376             this.collapse();
37377         }
37378     },
37379
37380     /**
37381      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37382      */
37383     expand : function(){
37384         if(this.isExpanded() || !this.hasFocus){
37385             return;
37386         }
37387         this.list.alignTo(this.el, this.listAlign);
37388         this.list.show();
37389         Roo.get(document).on('mousedown', this.collapseIf, this);
37390         Roo.get(document).on('mousewheel', this.collapseIf, this);
37391         this.fireEvent('expand', this);
37392     },
37393
37394     // private
37395     // Implements the default empty TriggerField.onTriggerClick function
37396     onTriggerClick : function(){
37397         if(this.disabled){
37398             return;
37399         }
37400         if(this.isExpanded()){
37401             this.collapse();
37402             if (!this.blockFocus) {
37403                 this.el.focus();
37404             }
37405             
37406         }else {
37407             this.hasFocus = true;
37408             if(this.triggerAction == 'all') {
37409                 this.doQuery(this.allQuery, true);
37410             } else {
37411                 this.doQuery(this.getRawValue());
37412             }
37413             if (!this.blockFocus) {
37414                 this.el.focus();
37415             }
37416         }
37417     }
37418
37419     /** @cfg {Boolean} grow @hide */
37420     /** @cfg {Number} growMin @hide */
37421     /** @cfg {Number} growMax @hide */
37422     /**
37423      * @hide
37424      * @method autoSize
37425      */
37426 });/*
37427  * Based on:
37428  * Ext JS Library 1.1.1
37429  * Copyright(c) 2006-2007, Ext JS, LLC.
37430  *
37431  * Originally Released Under LGPL - original licence link has changed is not relivant.
37432  *
37433  * Fork - LGPL
37434  * <script type="text/javascript">
37435  */
37436 /**
37437  * @class Roo.form.Checkbox
37438  * @extends Roo.form.Field
37439  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37440  * @constructor
37441  * Creates a new Checkbox
37442  * @param {Object} config Configuration options
37443  */
37444 Roo.form.Checkbox = function(config){
37445     Roo.form.Checkbox.superclass.constructor.call(this, config);
37446     this.addEvents({
37447         /**
37448          * @event check
37449          * Fires when the checkbox is checked or unchecked.
37450              * @param {Roo.form.Checkbox} this This checkbox
37451              * @param {Boolean} checked The new checked value
37452              */
37453         check : true
37454     });
37455 };
37456
37457 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37458     /**
37459      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37460      */
37461     focusClass : undefined,
37462     /**
37463      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37464      */
37465     fieldClass: "x-form-field",
37466     /**
37467      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37468      */
37469     checked: false,
37470     /**
37471      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37472      * {tag: "input", type: "checkbox", autocomplete: "off"})
37473      */
37474     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37475     /**
37476      * @cfg {String} boxLabel The text that appears beside the checkbox
37477      */
37478     boxLabel : "",
37479     /**
37480      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37481      */  
37482     inputValue : '1',
37483     /**
37484      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37485      */
37486      valueOff: '0', // value when not checked..
37487
37488     actionMode : 'viewEl', 
37489     //
37490     // private
37491     itemCls : 'x-menu-check-item x-form-item',
37492     groupClass : 'x-menu-group-item',
37493     inputType : 'hidden',
37494     
37495     
37496     inSetChecked: false, // check that we are not calling self...
37497     
37498     inputElement: false, // real input element?
37499     basedOn: false, // ????
37500     
37501     isFormField: true, // not sure where this is needed!!!!
37502
37503     onResize : function(){
37504         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37505         if(!this.boxLabel){
37506             this.el.alignTo(this.wrap, 'c-c');
37507         }
37508     },
37509
37510     initEvents : function(){
37511         Roo.form.Checkbox.superclass.initEvents.call(this);
37512         this.el.on("click", this.onClick,  this);
37513         this.el.on("change", this.onClick,  this);
37514     },
37515
37516
37517     getResizeEl : function(){
37518         return this.wrap;
37519     },
37520
37521     getPositionEl : function(){
37522         return this.wrap;
37523     },
37524
37525     // private
37526     onRender : function(ct, position){
37527         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37528         /*
37529         if(this.inputValue !== undefined){
37530             this.el.dom.value = this.inputValue;
37531         }
37532         */
37533         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37534         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37535         var viewEl = this.wrap.createChild({ 
37536             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37537         this.viewEl = viewEl;   
37538         this.wrap.on('click', this.onClick,  this); 
37539         
37540         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37541         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37542         
37543         
37544         
37545         if(this.boxLabel){
37546             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37547         //    viewEl.on('click', this.onClick,  this); 
37548         }
37549         //if(this.checked){
37550             this.setChecked(this.checked);
37551         //}else{
37552             //this.checked = this.el.dom;
37553         //}
37554
37555     },
37556
37557     // private
37558     initValue : Roo.emptyFn,
37559
37560     /**
37561      * Returns the checked state of the checkbox.
37562      * @return {Boolean} True if checked, else false
37563      */
37564     getValue : function(){
37565         if(this.el){
37566             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37567         }
37568         return this.valueOff;
37569         
37570     },
37571
37572         // private
37573     onClick : function(){ 
37574         this.setChecked(!this.checked);
37575
37576         //if(this.el.dom.checked != this.checked){
37577         //    this.setValue(this.el.dom.checked);
37578        // }
37579     },
37580
37581     /**
37582      * Sets the checked state of the checkbox.
37583      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37584      */
37585     setValue : function(v,suppressEvent){
37586         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37587         //if(this.el && this.el.dom){
37588         //    this.el.dom.checked = this.checked;
37589         //    this.el.dom.defaultChecked = this.checked;
37590         //}
37591         this.setChecked(v === this.inputValue);
37592         //this.fireEvent("check", this, this.checked);
37593     },
37594     // private..
37595     setChecked : function(state,suppressEvent)
37596     {
37597         if (this.inSetChecked) {
37598             this.checked = state;
37599             return;
37600         }
37601         
37602     
37603         if(this.wrap){
37604             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37605         }
37606         this.checked = state;
37607         if(suppressEvent !== true){
37608             this.fireEvent('checkchange', this, state);
37609         }
37610         this.inSetChecked = true;
37611         this.el.dom.value = state ? this.inputValue : this.valueOff;
37612         this.inSetChecked = false;
37613         
37614     },
37615     // handle setting of hidden value by some other method!!?!?
37616     setFromHidden: function()
37617     {
37618         if(!this.el){
37619             return;
37620         }
37621         //console.log("SET FROM HIDDEN");
37622         //alert('setFrom hidden');
37623         this.setValue(this.el.dom.value);
37624     },
37625     
37626     onDestroy : function()
37627     {
37628         if(this.viewEl){
37629             Roo.get(this.viewEl).remove();
37630         }
37631          
37632         Roo.form.Checkbox.superclass.onDestroy.call(this);
37633     }
37634
37635 });/*
37636  * Based on:
37637  * Ext JS Library 1.1.1
37638  * Copyright(c) 2006-2007, Ext JS, LLC.
37639  *
37640  * Originally Released Under LGPL - original licence link has changed is not relivant.
37641  *
37642  * Fork - LGPL
37643  * <script type="text/javascript">
37644  */
37645  
37646 /**
37647  * @class Roo.form.Radio
37648  * @extends Roo.form.Checkbox
37649  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37650  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37651  * @constructor
37652  * Creates a new Radio
37653  * @param {Object} config Configuration options
37654  */
37655 Roo.form.Radio = function(){
37656     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37657 };
37658 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37659     inputType: 'radio',
37660
37661     /**
37662      * If this radio is part of a group, it will return the selected value
37663      * @return {String}
37664      */
37665     getGroupValue : function(){
37666         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37667     }
37668 });//<script type="text/javascript">
37669
37670 /*
37671  * Ext JS Library 1.1.1
37672  * Copyright(c) 2006-2007, Ext JS, LLC.
37673  * licensing@extjs.com
37674  * 
37675  * http://www.extjs.com/license
37676  */
37677  
37678  /**
37679   * 
37680   * Known bugs:
37681   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37682   * - IE ? - no idea how much works there.
37683   * 
37684   * 
37685   * 
37686   */
37687  
37688
37689 /**
37690  * @class Ext.form.HtmlEditor
37691  * @extends Ext.form.Field
37692  * Provides a lightweight HTML Editor component.
37693  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37694  * 
37695  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37696  * supported by this editor.</b><br/><br/>
37697  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37698  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37699  */
37700 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37701       /**
37702      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37703      */
37704     toolbars : false,
37705     /**
37706      * @cfg {String} createLinkText The default text for the create link prompt
37707      */
37708     createLinkText : 'Please enter the URL for the link:',
37709     /**
37710      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37711      */
37712     defaultLinkValue : 'http:/'+'/',
37713    
37714     
37715     // id of frame..
37716     frameId: false,
37717     
37718     // private properties
37719     validationEvent : false,
37720     deferHeight: true,
37721     initialized : false,
37722     activated : false,
37723     sourceEditMode : false,
37724     onFocus : Roo.emptyFn,
37725     iframePad:3,
37726     hideMode:'offsets',
37727     defaultAutoCreate : {
37728         tag: "textarea",
37729         style:"width:500px;height:300px;",
37730         autocomplete: "off"
37731     },
37732
37733     // private
37734     initComponent : function(){
37735         this.addEvents({
37736             /**
37737              * @event initialize
37738              * Fires when the editor is fully initialized (including the iframe)
37739              * @param {HtmlEditor} this
37740              */
37741             initialize: true,
37742             /**
37743              * @event activate
37744              * Fires when the editor is first receives the focus. Any insertion must wait
37745              * until after this event.
37746              * @param {HtmlEditor} this
37747              */
37748             activate: true,
37749              /**
37750              * @event beforesync
37751              * Fires before the textarea is updated with content from the editor iframe. Return false
37752              * to cancel the sync.
37753              * @param {HtmlEditor} this
37754              * @param {String} html
37755              */
37756             beforesync: true,
37757              /**
37758              * @event beforepush
37759              * Fires before the iframe editor is updated with content from the textarea. Return false
37760              * to cancel the push.
37761              * @param {HtmlEditor} this
37762              * @param {String} html
37763              */
37764             beforepush: true,
37765              /**
37766              * @event sync
37767              * Fires when the textarea is updated with content from the editor iframe.
37768              * @param {HtmlEditor} this
37769              * @param {String} html
37770              */
37771             sync: true,
37772              /**
37773              * @event push
37774              * Fires when the iframe editor is updated with content from the textarea.
37775              * @param {HtmlEditor} this
37776              * @param {String} html
37777              */
37778             push: true,
37779              /**
37780              * @event editmodechange
37781              * Fires when the editor switches edit modes
37782              * @param {HtmlEditor} this
37783              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37784              */
37785             editmodechange: true,
37786             /**
37787              * @event editorevent
37788              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37789              * @param {HtmlEditor} this
37790              */
37791             editorevent: true
37792         })
37793     },
37794
37795     /**
37796      * Protected method that will not generally be called directly. It
37797      * is called when the editor creates its toolbar. Override this method if you need to
37798      * add custom toolbar buttons.
37799      * @param {HtmlEditor} editor
37800      */
37801     createToolbar : function(editor){
37802         if (!editor.toolbars || !editor.toolbars.length) {
37803             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37804         }
37805         
37806         for (var i =0 ; i < editor.toolbars.length;i++) {
37807             editor.toolbars[i].init(editor);
37808         }
37809          
37810         
37811     },
37812
37813     /**
37814      * Protected method that will not generally be called directly. It
37815      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37816      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37817      */
37818     getDocMarkup : function(){
37819         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37820     },
37821
37822     // private
37823     onRender : function(ct, position){
37824         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37825         this.el.dom.style.border = '0 none';
37826         this.el.dom.setAttribute('tabIndex', -1);
37827         this.el.addClass('x-hidden');
37828         if(Roo.isIE){ // fix IE 1px bogus margin
37829             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37830         }
37831         this.wrap = this.el.wrap({
37832             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37833         });
37834
37835         this.frameId = Roo.id();
37836         this.createToolbar(this);
37837         
37838         
37839         
37840         
37841       
37842         
37843         var iframe = this.wrap.createChild({
37844             tag: 'iframe',
37845             id: this.frameId,
37846             name: this.frameId,
37847             frameBorder : 'no',
37848             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37849         });
37850         
37851        // console.log(iframe);
37852         //this.wrap.dom.appendChild(iframe);
37853
37854         this.iframe = iframe.dom;
37855
37856          this.assignDocWin();
37857         
37858         this.doc.designMode = 'on';
37859        
37860         this.doc.open();
37861         this.doc.write(this.getDocMarkup());
37862         this.doc.close();
37863
37864         
37865         var task = { // must defer to wait for browser to be ready
37866             run : function(){
37867                 //console.log("run task?" + this.doc.readyState);
37868                 this.assignDocWin();
37869                 if(this.doc.body || this.doc.readyState == 'complete'){
37870                     try {
37871                         
37872                        
37873                         this.doc.designMode="on";
37874                     } catch (e) {
37875                         return;
37876                     }
37877                     Roo.TaskMgr.stop(task);
37878                     this.initEditor.defer(10, this);
37879                 }
37880             },
37881             interval : 10,
37882             duration:10000,
37883             scope: this
37884         };
37885         Roo.TaskMgr.start(task);
37886
37887         if(!this.width){
37888             this.setSize(this.el.getSize());
37889         }
37890     },
37891
37892     // private
37893     onResize : function(w, h){
37894         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
37895         if(this.el && this.iframe){
37896             if(typeof w == 'number'){
37897                 var aw = w - this.wrap.getFrameWidth('lr');
37898                 this.el.setWidth(this.adjustWidth('textarea', aw));
37899                 this.iframe.style.width = aw + 'px';
37900             }
37901             if(typeof h == 'number'){
37902                 var tbh = 0;
37903                 for (var i =0; i < this.toolbars.length;i++) {
37904                     // fixme - ask toolbars for heights?
37905                     tbh += this.toolbars[i].tb.el.getHeight();
37906                 }
37907                 
37908                 
37909                 
37910                 
37911                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
37912                 this.el.setHeight(this.adjustWidth('textarea', ah));
37913                 this.iframe.style.height = ah + 'px';
37914                 if(this.doc){
37915                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
37916                 }
37917             }
37918         }
37919     },
37920
37921     /**
37922      * Toggles the editor between standard and source edit mode.
37923      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
37924      */
37925     toggleSourceEdit : function(sourceEditMode){
37926         
37927         this.sourceEditMode = sourceEditMode === true;
37928         
37929         if(this.sourceEditMode){
37930           
37931             this.syncValue();
37932             this.iframe.className = 'x-hidden';
37933             this.el.removeClass('x-hidden');
37934             this.el.dom.removeAttribute('tabIndex');
37935             this.el.focus();
37936         }else{
37937              
37938             this.pushValue();
37939             this.iframe.className = '';
37940             this.el.addClass('x-hidden');
37941             this.el.dom.setAttribute('tabIndex', -1);
37942             this.deferFocus();
37943         }
37944         this.setSize(this.wrap.getSize());
37945         this.fireEvent('editmodechange', this, this.sourceEditMode);
37946     },
37947
37948     // private used internally
37949     createLink : function(){
37950         var url = prompt(this.createLinkText, this.defaultLinkValue);
37951         if(url && url != 'http:/'+'/'){
37952             this.relayCmd('createlink', url);
37953         }
37954     },
37955
37956     // private (for BoxComponent)
37957     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37958
37959     // private (for BoxComponent)
37960     getResizeEl : function(){
37961         return this.wrap;
37962     },
37963
37964     // private (for BoxComponent)
37965     getPositionEl : function(){
37966         return this.wrap;
37967     },
37968
37969     // private
37970     initEvents : function(){
37971         this.originalValue = this.getValue();
37972     },
37973
37974     /**
37975      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
37976      * @method
37977      */
37978     markInvalid : Roo.emptyFn,
37979     /**
37980      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
37981      * @method
37982      */
37983     clearInvalid : Roo.emptyFn,
37984
37985     setValue : function(v){
37986         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
37987         this.pushValue();
37988     },
37989
37990     /**
37991      * Protected method that will not generally be called directly. If you need/want
37992      * custom HTML cleanup, this is the method you should override.
37993      * @param {String} html The HTML to be cleaned
37994      * return {String} The cleaned HTML
37995      */
37996     cleanHtml : function(html){
37997         html = String(html);
37998         if(html.length > 5){
37999             if(Roo.isSafari){ // strip safari nonsense
38000                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38001             }
38002         }
38003         if(html == '&nbsp;'){
38004             html = '';
38005         }
38006         return html;
38007     },
38008
38009     /**
38010      * Protected method that will not generally be called directly. Syncs the contents
38011      * of the editor iframe with the textarea.
38012      */
38013     syncValue : function(){
38014         if(this.initialized){
38015             var bd = (this.doc.body || this.doc.documentElement);
38016             var html = bd.innerHTML;
38017             if(Roo.isSafari){
38018                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38019                 var m = bs.match(/text-align:(.*?);/i);
38020                 if(m && m[1]){
38021                     html = '<div style="'+m[0]+'">' + html + '</div>';
38022                 }
38023             }
38024             html = this.cleanHtml(html);
38025             if(this.fireEvent('beforesync', this, html) !== false){
38026                 this.el.dom.value = html;
38027                 this.fireEvent('sync', this, html);
38028             }
38029         }
38030     },
38031
38032     /**
38033      * Protected method that will not generally be called directly. Pushes the value of the textarea
38034      * into the iframe editor.
38035      */
38036     pushValue : function(){
38037         if(this.initialized){
38038             var v = this.el.dom.value;
38039             if(v.length < 1){
38040                 v = '&#160;';
38041             }
38042             if(this.fireEvent('beforepush', this, v) !== false){
38043                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38044                 this.fireEvent('push', this, v);
38045             }
38046         }
38047     },
38048
38049     // private
38050     deferFocus : function(){
38051         this.focus.defer(10, this);
38052     },
38053
38054     // doc'ed in Field
38055     focus : function(){
38056         if(this.win && !this.sourceEditMode){
38057             this.win.focus();
38058         }else{
38059             this.el.focus();
38060         }
38061     },
38062     
38063     assignDocWin: function()
38064     {
38065         var iframe = this.iframe;
38066         
38067          if(Roo.isIE){
38068             this.doc = iframe.contentWindow.document;
38069             this.win = iframe.contentWindow;
38070         } else {
38071             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38072             this.win = Roo.get(this.frameId).dom.contentWindow;
38073         }
38074     },
38075     
38076     // private
38077     initEditor : function(){
38078         //console.log("INIT EDITOR");
38079         this.assignDocWin();
38080         
38081         
38082         
38083         this.doc.designMode="on";
38084         this.doc.open();
38085         this.doc.write(this.getDocMarkup());
38086         this.doc.close();
38087         
38088         var dbody = (this.doc.body || this.doc.documentElement);
38089         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38090         // this copies styles from the containing element into thsi one..
38091         // not sure why we need all of this..
38092         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38093         ss['background-attachment'] = 'fixed'; // w3c
38094         dbody.bgProperties = 'fixed'; // ie
38095         Roo.DomHelper.applyStyles(dbody, ss);
38096         Roo.EventManager.on(this.doc, {
38097             'mousedown': this.onEditorEvent,
38098             'dblclick': this.onEditorEvent,
38099             'click': this.onEditorEvent,
38100             'keyup': this.onEditorEvent,
38101             buffer:100,
38102             scope: this
38103         });
38104         if(Roo.isGecko){
38105             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38106         }
38107         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38108             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38109         }
38110         this.initialized = true;
38111
38112         this.fireEvent('initialize', this);
38113         this.pushValue();
38114     },
38115
38116     // private
38117     onDestroy : function(){
38118         
38119         
38120         
38121         if(this.rendered){
38122             
38123             for (var i =0; i < this.toolbars.length;i++) {
38124                 // fixme - ask toolbars for heights?
38125                 this.toolbars[i].onDestroy();
38126             }
38127             
38128             this.wrap.dom.innerHTML = '';
38129             this.wrap.remove();
38130         }
38131     },
38132
38133     // private
38134     onFirstFocus : function(){
38135         
38136         this.assignDocWin();
38137         
38138         
38139         this.activated = true;
38140         for (var i =0; i < this.toolbars.length;i++) {
38141             this.toolbars[i].onFirstFocus();
38142         }
38143        
38144         if(Roo.isGecko){ // prevent silly gecko errors
38145             this.win.focus();
38146             var s = this.win.getSelection();
38147             if(!s.focusNode || s.focusNode.nodeType != 3){
38148                 var r = s.getRangeAt(0);
38149                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38150                 r.collapse(true);
38151                 this.deferFocus();
38152             }
38153             try{
38154                 this.execCmd('useCSS', true);
38155                 this.execCmd('styleWithCSS', false);
38156             }catch(e){}
38157         }
38158         this.fireEvent('activate', this);
38159     },
38160
38161     // private
38162     adjustFont: function(btn){
38163         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38164         //if(Roo.isSafari){ // safari
38165         //    adjust *= 2;
38166        // }
38167         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38168         if(Roo.isSafari){ // safari
38169             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38170             v =  (v < 10) ? 10 : v;
38171             v =  (v > 48) ? 48 : v;
38172             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38173             
38174         }
38175         
38176         
38177         v = Math.max(1, v+adjust);
38178         
38179         this.execCmd('FontSize', v  );
38180     },
38181
38182     onEditorEvent : function(e){
38183         this.fireEvent('editorevent', this, e);
38184       //  this.updateToolbar();
38185         this.syncValue();
38186     },
38187
38188     insertTag : function(tg)
38189     {
38190         // could be a bit smarter... -> wrap the current selected tRoo..
38191         
38192         this.execCmd("formatblock",   tg);
38193         
38194     },
38195     
38196     insertText : function(txt)
38197     {
38198         
38199         
38200         range = this.createRange();
38201         range.deleteContents();
38202                //alert(Sender.getAttribute('label'));
38203                
38204         range.insertNode(this.doc.createTextNode(txt));
38205     } ,
38206     
38207     // private
38208     relayBtnCmd : function(btn){
38209         this.relayCmd(btn.cmd);
38210     },
38211
38212     /**
38213      * Executes a Midas editor command on the editor document and performs necessary focus and
38214      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38215      * @param {String} cmd The Midas command
38216      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38217      */
38218     relayCmd : function(cmd, value){
38219         this.win.focus();
38220         this.execCmd(cmd, value);
38221         this.fireEvent('editorevent', this);
38222         //this.updateToolbar();
38223         this.deferFocus();
38224     },
38225
38226     /**
38227      * Executes a Midas editor command directly on the editor document.
38228      * For visual commands, you should use {@link #relayCmd} instead.
38229      * <b>This should only be called after the editor is initialized.</b>
38230      * @param {String} cmd The Midas command
38231      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38232      */
38233     execCmd : function(cmd, value){
38234         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38235         this.syncValue();
38236     },
38237
38238     // private
38239     applyCommand : function(e){
38240         if(e.ctrlKey){
38241             var c = e.getCharCode(), cmd;
38242             if(c > 0){
38243                 c = String.fromCharCode(c);
38244                 switch(c){
38245                     case 'b':
38246                         cmd = 'bold';
38247                     break;
38248                     case 'i':
38249                         cmd = 'italic';
38250                     break;
38251                     case 'u':
38252                         cmd = 'underline';
38253                     break;
38254                 }
38255                 if(cmd){
38256                     this.win.focus();
38257                     this.execCmd(cmd);
38258                     this.deferFocus();
38259                     e.preventDefault();
38260                 }
38261             }
38262         }
38263     },
38264
38265     /**
38266      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38267      * to insert tRoo.
38268      * @param {String} text
38269      */
38270     insertAtCursor : function(text){
38271         if(!this.activated){
38272             return;
38273         }
38274         if(Roo.isIE){
38275             this.win.focus();
38276             var r = this.doc.selection.createRange();
38277             if(r){
38278                 r.collapse(true);
38279                 r.pasteHTML(text);
38280                 this.syncValue();
38281                 this.deferFocus();
38282             }
38283         }else if(Roo.isGecko || Roo.isOpera){
38284             this.win.focus();
38285             this.execCmd('InsertHTML', text);
38286             this.deferFocus();
38287         }else if(Roo.isSafari){
38288             this.execCmd('InsertText', text);
38289             this.deferFocus();
38290         }
38291     },
38292
38293     // private
38294     fixKeys : function(){ // load time branching for fastest keydown performance
38295         if(Roo.isIE){
38296             return function(e){
38297                 var k = e.getKey(), r;
38298                 if(k == e.TAB){
38299                     e.stopEvent();
38300                     r = this.doc.selection.createRange();
38301                     if(r){
38302                         r.collapse(true);
38303                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38304                         this.deferFocus();
38305                     }
38306                 }else if(k == e.ENTER){
38307                     r = this.doc.selection.createRange();
38308                     if(r){
38309                         var target = r.parentElement();
38310                         if(!target || target.tagName.toLowerCase() != 'li'){
38311                             e.stopEvent();
38312                             r.pasteHTML('<br />');
38313                             r.collapse(false);
38314                             r.select();
38315                         }
38316                     }
38317                 }
38318             };
38319         }else if(Roo.isOpera){
38320             return function(e){
38321                 var k = e.getKey();
38322                 if(k == e.TAB){
38323                     e.stopEvent();
38324                     this.win.focus();
38325                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38326                     this.deferFocus();
38327                 }
38328             };
38329         }else if(Roo.isSafari){
38330             return function(e){
38331                 var k = e.getKey();
38332                 if(k == e.TAB){
38333                     e.stopEvent();
38334                     this.execCmd('InsertText','\t');
38335                     this.deferFocus();
38336                 }
38337              };
38338         }
38339     }(),
38340     
38341     getAllAncestors: function()
38342     {
38343         var p = this.getSelectedNode();
38344         var a = [];
38345         if (!p) {
38346             a.push(p); // push blank onto stack..
38347             p = this.getParentElement();
38348         }
38349         
38350         
38351         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38352             a.push(p);
38353             p = p.parentNode;
38354         }
38355         a.push(this.doc.body);
38356         return a;
38357     },
38358     lastSel : false,
38359     lastSelNode : false,
38360     
38361     
38362     getSelection : function() 
38363     {
38364         this.assignDocWin();
38365         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38366     },
38367     
38368     getSelectedNode: function() 
38369     {
38370         // this may only work on Gecko!!!
38371         
38372         // should we cache this!!!!
38373         
38374         
38375         
38376          
38377         var range = this.createRange(this.getSelection());
38378         
38379         if (Roo.isIE) {
38380             var parent = range.parentElement();
38381             while (true) {
38382                 var testRange = range.duplicate();
38383                 testRange.moveToElementText(parent);
38384                 if (testRange.inRange(range)) {
38385                     break;
38386                 }
38387                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38388                     break;
38389                 }
38390                 parent = parent.parentElement;
38391             }
38392             return parent;
38393         }
38394         
38395         
38396         var ar = range.endContainer.childNodes;
38397         if (!ar.length) {
38398             ar = range.commonAncestorContainer.childNodes;
38399             //alert(ar.length);
38400         }
38401         var nodes = [];
38402         var other_nodes = [];
38403         var has_other_nodes = false;
38404         for (var i=0;i<ar.length;i++) {
38405             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38406                 continue;
38407             }
38408             // fullly contained node.
38409             
38410             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38411                 nodes.push(ar[i]);
38412                 continue;
38413             }
38414             
38415             // probably selected..
38416             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38417                 other_nodes.push(ar[i]);
38418                 continue;
38419             }
38420             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38421                 continue;
38422             }
38423             
38424             
38425             has_other_nodes = true;
38426         }
38427         if (!nodes.length && other_nodes.length) {
38428             nodes= other_nodes;
38429         }
38430         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38431             return false;
38432         }
38433         
38434         return nodes[0];
38435     },
38436     createRange: function(sel)
38437     {
38438         // this has strange effects when using with 
38439         // top toolbar - not sure if it's a great idea.
38440         //this.editor.contentWindow.focus();
38441         if (typeof sel != "undefined") {
38442             try {
38443                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38444             } catch(e) {
38445                 return this.doc.createRange();
38446             }
38447         } else {
38448             return this.doc.createRange();
38449         }
38450     },
38451     getParentElement: function()
38452     {
38453         
38454         this.assignDocWin();
38455         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38456         
38457         var range = this.createRange(sel);
38458          
38459         try {
38460             var p = range.commonAncestorContainer;
38461             while (p.nodeType == 3) { // text node
38462                 p = p.parentNode;
38463             }
38464             return p;
38465         } catch (e) {
38466             return null;
38467         }
38468     
38469     },
38470     
38471     
38472     
38473     // BC Hacks - cause I cant work out what i was trying to do..
38474     rangeIntersectsNode : function(range, node)
38475     {
38476         var nodeRange = node.ownerDocument.createRange();
38477         try {
38478             nodeRange.selectNode(node);
38479         }
38480         catch (e) {
38481             nodeRange.selectNodeContents(node);
38482         }
38483
38484         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38485                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38486     },
38487     rangeCompareNode : function(range, node) {
38488         var nodeRange = node.ownerDocument.createRange();
38489         try {
38490             nodeRange.selectNode(node);
38491         } catch (e) {
38492             nodeRange.selectNodeContents(node);
38493         }
38494         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38495         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38496
38497         if (nodeIsBefore && !nodeIsAfter)
38498             return 0;
38499         if (!nodeIsBefore && nodeIsAfter)
38500             return 1;
38501         if (nodeIsBefore && nodeIsAfter)
38502             return 2;
38503
38504         return 3;
38505     }
38506
38507     
38508     
38509     // hide stuff that is not compatible
38510     /**
38511      * @event blur
38512      * @hide
38513      */
38514     /**
38515      * @event change
38516      * @hide
38517      */
38518     /**
38519      * @event focus
38520      * @hide
38521      */
38522     /**
38523      * @event specialkey
38524      * @hide
38525      */
38526     /**
38527      * @cfg {String} fieldClass @hide
38528      */
38529     /**
38530      * @cfg {String} focusClass @hide
38531      */
38532     /**
38533      * @cfg {String} autoCreate @hide
38534      */
38535     /**
38536      * @cfg {String} inputType @hide
38537      */
38538     /**
38539      * @cfg {String} invalidClass @hide
38540      */
38541     /**
38542      * @cfg {String} invalidText @hide
38543      */
38544     /**
38545      * @cfg {String} msgFx @hide
38546      */
38547     /**
38548      * @cfg {String} validateOnBlur @hide
38549      */
38550 });// <script type="text/javascript">
38551 /*
38552  * Based on
38553  * Ext JS Library 1.1.1
38554  * Copyright(c) 2006-2007, Ext JS, LLC.
38555  *  
38556  
38557  */
38558
38559 /**
38560  * @class Roo.form.HtmlEditorToolbar1
38561  * Basic Toolbar
38562  * 
38563  * Usage:
38564  *
38565  new Roo.form.HtmlEditor({
38566     ....
38567     toolbars : [
38568         new Roo.form.HtmlEditorToolbar1({
38569             disable : { fonts: 1 , format: 1, ..., ... , ...],
38570             btns : [ .... ]
38571         })
38572     }
38573      
38574  * 
38575  * @cfg {Object} disable List of elements to disable..
38576  * @cfg {Array} btns List of additional buttons.
38577  * 
38578  * 
38579  * NEEDS Extra CSS? 
38580  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38581  */
38582  
38583 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38584 {
38585     
38586     Roo.apply(this, config);
38587     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38588     // dont call parent... till later.
38589 }
38590
38591 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38592     
38593     tb: false,
38594     
38595     rendered: false,
38596     
38597     editor : false,
38598     /**
38599      * @cfg {Object} disable  List of toolbar elements to disable
38600          
38601      */
38602     disable : false,
38603       /**
38604      * @cfg {Array} fontFamilies An array of available font families
38605      */
38606     fontFamilies : [
38607         'Arial',
38608         'Courier New',
38609         'Tahoma',
38610         'Times New Roman',
38611         'Verdana'
38612     ],
38613     
38614     specialChars : [
38615            "&#169;",
38616           "&#174;",     
38617           "&#8482;",    
38618           "&#163;" ,    
38619          // "&#8212;",    
38620           "&#8230;",    
38621           "&#247;" ,    
38622         //  "&#225;" ,     ?? a acute?
38623            "&#8364;"    , //Euro
38624        //   "&#8220;"    ,
38625         //  "&#8221;"    ,
38626         //  "&#8226;"    ,
38627           "&#176;"  //   , // degrees
38628
38629          // "&#233;"     , // e ecute
38630          // "&#250;"     , // u ecute?
38631     ],
38632     inputElements : [ 
38633             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38634             "input:submit", "input:button", "select", "textarea", "label" ],
38635     formats : [
38636         ["p"] ,  
38637         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38638         ["pre"],[ "code"], 
38639         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38640     ],
38641      /**
38642      * @cfg {String} defaultFont default font to use.
38643      */
38644     defaultFont: 'tahoma',
38645    
38646     fontSelect : false,
38647     
38648     
38649     formatCombo : false,
38650     
38651     init : function(editor)
38652     {
38653         this.editor = editor;
38654         
38655         
38656         var fid = editor.frameId;
38657         var etb = this;
38658         function btn(id, toggle, handler){
38659             var xid = fid + '-'+ id ;
38660             return {
38661                 id : xid,
38662                 cmd : id,
38663                 cls : 'x-btn-icon x-edit-'+id,
38664                 enableToggle:toggle !== false,
38665                 scope: editor, // was editor...
38666                 handler:handler||editor.relayBtnCmd,
38667                 clickEvent:'mousedown',
38668                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38669                 tabIndex:-1
38670             };
38671         }
38672         
38673         
38674         
38675         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38676         this.tb = tb;
38677          // stop form submits
38678         tb.el.on('click', function(e){
38679             e.preventDefault(); // what does this do?
38680         });
38681
38682         if(!this.disable.font && !Roo.isSafari){
38683             /* why no safari for fonts
38684             editor.fontSelect = tb.el.createChild({
38685                 tag:'select',
38686                 tabIndex: -1,
38687                 cls:'x-font-select',
38688                 html: editor.createFontOptions()
38689             });
38690             editor.fontSelect.on('change', function(){
38691                 var font = editor.fontSelect.dom.value;
38692                 editor.relayCmd('fontname', font);
38693                 editor.deferFocus();
38694             }, editor);
38695             tb.add(
38696                 editor.fontSelect.dom,
38697                 '-'
38698             );
38699             */
38700         };
38701         if(!this.disable.formats){
38702             this.formatCombo = new Roo.form.ComboBox({
38703                 store: new Roo.data.SimpleStore({
38704                     id : 'tag',
38705                     fields: ['tag'],
38706                     data : this.formats // from states.js
38707                 }),
38708                 blockFocus : true,
38709                 //autoCreate : {tag: "div",  size: "20"},
38710                 displayField:'tag',
38711                 typeAhead: false,
38712                 mode: 'local',
38713                 editable : false,
38714                 triggerAction: 'all',
38715                 emptyText:'Add tag',
38716                 selectOnFocus:true,
38717                 width:135,
38718                 listeners : {
38719                     'select': function(c, r, i) {
38720                         editor.insertTag(r.get('tag'));
38721                         editor.focus();
38722                     }
38723                 }
38724
38725             });
38726             tb.addField(this.formatCombo);
38727             
38728         }
38729         
38730         if(!this.disable.format){
38731             tb.add(
38732                 btn('bold'),
38733                 btn('italic'),
38734                 btn('underline')
38735             );
38736         };
38737         if(!this.disable.fontSize){
38738             tb.add(
38739                 '-',
38740                 
38741                 
38742                 btn('increasefontsize', false, editor.adjustFont),
38743                 btn('decreasefontsize', false, editor.adjustFont)
38744             );
38745         };
38746         
38747         
38748         if(this.disable.colors){
38749             tb.add(
38750                 '-', {
38751                     id:editor.frameId +'-forecolor',
38752                     cls:'x-btn-icon x-edit-forecolor',
38753                     clickEvent:'mousedown',
38754                     tooltip: this.buttonTips['forecolor'] || undefined,
38755                     tabIndex:-1,
38756                     menu : new Roo.menu.ColorMenu({
38757                         allowReselect: true,
38758                         focus: Roo.emptyFn,
38759                         value:'000000',
38760                         plain:true,
38761                         selectHandler: function(cp, color){
38762                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38763                             editor.deferFocus();
38764                         },
38765                         scope: editor,
38766                         clickEvent:'mousedown'
38767                     })
38768                 }, {
38769                     id:editor.frameId +'backcolor',
38770                     cls:'x-btn-icon x-edit-backcolor',
38771                     clickEvent:'mousedown',
38772                     tooltip: this.buttonTips['backcolor'] || undefined,
38773                     tabIndex:-1,
38774                     menu : new Roo.menu.ColorMenu({
38775                         focus: Roo.emptyFn,
38776                         value:'FFFFFF',
38777                         plain:true,
38778                         allowReselect: true,
38779                         selectHandler: function(cp, color){
38780                             if(Roo.isGecko){
38781                                 editor.execCmd('useCSS', false);
38782                                 editor.execCmd('hilitecolor', color);
38783                                 editor.execCmd('useCSS', true);
38784                                 editor.deferFocus();
38785                             }else{
38786                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38787                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38788                                 editor.deferFocus();
38789                             }
38790                         },
38791                         scope:editor,
38792                         clickEvent:'mousedown'
38793                     })
38794                 }
38795             );
38796         };
38797         // now add all the items...
38798         
38799
38800         if(!this.disable.alignments){
38801             tb.add(
38802                 '-',
38803                 btn('justifyleft'),
38804                 btn('justifycenter'),
38805                 btn('justifyright')
38806             );
38807         };
38808
38809         //if(!Roo.isSafari){
38810             if(!this.disable.links){
38811                 tb.add(
38812                     '-',
38813                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38814                 );
38815             };
38816
38817             if(!this.disable.lists){
38818                 tb.add(
38819                     '-',
38820                     btn('insertorderedlist'),
38821                     btn('insertunorderedlist')
38822                 );
38823             }
38824             if(!this.disable.sourceEdit){
38825                 tb.add(
38826                     '-',
38827                     btn('sourceedit', true, function(btn){
38828                         this.toggleSourceEdit(btn.pressed);
38829                     })
38830                 );
38831             }
38832         //}
38833         
38834         var smenu = { };
38835         // special menu.. - needs to be tidied up..
38836         if (!this.disable.special) {
38837             smenu = {
38838                 text: "&#169;",
38839                 cls: 'x-edit-none',
38840                 menu : {
38841                     items : []
38842                    }
38843             };
38844             for (var i =0; i < this.specialChars.length; i++) {
38845                 smenu.menu.items.push({
38846                     
38847                     text: this.specialChars[i],
38848                     handler: function(a,b) {
38849                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38850                     },
38851                     tabIndex:-1
38852                 });
38853             }
38854             
38855             
38856             tb.add(smenu);
38857             
38858             
38859         }
38860         if (this.btns) {
38861             for(var i =0; i< this.btns.length;i++) {
38862                 var b = this.btns[i];
38863                 b.cls =  'x-edit-none';
38864                 b.scope = editor;
38865                 tb.add(b);
38866             }
38867         
38868         }
38869         
38870         
38871         
38872         // disable everything...
38873         
38874         this.tb.items.each(function(item){
38875            if(item.id != editor.frameId+ '-sourceedit'){
38876                 item.disable();
38877             }
38878         });
38879         this.rendered = true;
38880         
38881         // the all the btns;
38882         editor.on('editorevent', this.updateToolbar, this);
38883         // other toolbars need to implement this..
38884         //editor.on('editmodechange', this.updateToolbar, this);
38885     },
38886     
38887     
38888     
38889     /**
38890      * Protected method that will not generally be called directly. It triggers
38891      * a toolbar update by reading the markup state of the current selection in the editor.
38892      */
38893     updateToolbar: function(){
38894
38895         if(!this.editor.activated){
38896             this.editor.onFirstFocus();
38897             return;
38898         }
38899
38900         var btns = this.tb.items.map, 
38901             doc = this.editor.doc,
38902             frameId = this.editor.frameId;
38903
38904         if(!this.disable.font && !Roo.isSafari){
38905             /*
38906             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
38907             if(name != this.fontSelect.dom.value){
38908                 this.fontSelect.dom.value = name;
38909             }
38910             */
38911         }
38912         if(!this.disable.format){
38913             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
38914             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
38915             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
38916         }
38917         if(!this.disable.alignments){
38918             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
38919             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
38920             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
38921         }
38922         if(!Roo.isSafari && !this.disable.lists){
38923             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
38924             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
38925         }
38926         
38927         var ans = this.editor.getAllAncestors();
38928         if (this.formatCombo) {
38929             
38930             
38931             var store = this.formatCombo.store;
38932             this.formatCombo.setValue("");
38933             for (var i =0; i < ans.length;i++) {
38934                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
38935                     // select it..
38936                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
38937                     break;
38938                 }
38939             }
38940         }
38941         
38942         
38943         
38944         // hides menus... - so this cant be on a menu...
38945         Roo.menu.MenuMgr.hideAll();
38946
38947         //this.editorsyncValue();
38948     },
38949    
38950     
38951     createFontOptions : function(){
38952         var buf = [], fs = this.fontFamilies, ff, lc;
38953         for(var i = 0, len = fs.length; i< len; i++){
38954             ff = fs[i];
38955             lc = ff.toLowerCase();
38956             buf.push(
38957                 '<option value="',lc,'" style="font-family:',ff,';"',
38958                     (this.defaultFont == lc ? ' selected="true">' : '>'),
38959                     ff,
38960                 '</option>'
38961             );
38962         }
38963         return buf.join('');
38964     },
38965     
38966     toggleSourceEdit : function(sourceEditMode){
38967         if(sourceEditMode === undefined){
38968             sourceEditMode = !this.sourceEditMode;
38969         }
38970         this.sourceEditMode = sourceEditMode === true;
38971         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
38972         // just toggle the button?
38973         if(btn.pressed !== this.editor.sourceEditMode){
38974             btn.toggle(this.editor.sourceEditMode);
38975             return;
38976         }
38977         
38978         if(this.sourceEditMode){
38979             this.tb.items.each(function(item){
38980                 if(item.cmd != 'sourceedit'){
38981                     item.disable();
38982                 }
38983             });
38984           
38985         }else{
38986             if(this.initialized){
38987                 this.tb.items.each(function(item){
38988                     item.enable();
38989                 });
38990             }
38991             
38992         }
38993         // tell the editor that it's been pressed..
38994         this.editor.toggleSourceEdit(sourceEditMode);
38995        
38996     },
38997      /**
38998      * Object collection of toolbar tooltips for the buttons in the editor. The key
38999      * is the command id associated with that button and the value is a valid QuickTips object.
39000      * For example:
39001 <pre><code>
39002 {
39003     bold : {
39004         title: 'Bold (Ctrl+B)',
39005         text: 'Make the selected text bold.',
39006         cls: 'x-html-editor-tip'
39007     },
39008     italic : {
39009         title: 'Italic (Ctrl+I)',
39010         text: 'Make the selected text italic.',
39011         cls: 'x-html-editor-tip'
39012     },
39013     ...
39014 </code></pre>
39015     * @type Object
39016      */
39017     buttonTips : {
39018         bold : {
39019             title: 'Bold (Ctrl+B)',
39020             text: 'Make the selected text bold.',
39021             cls: 'x-html-editor-tip'
39022         },
39023         italic : {
39024             title: 'Italic (Ctrl+I)',
39025             text: 'Make the selected text italic.',
39026             cls: 'x-html-editor-tip'
39027         },
39028         underline : {
39029             title: 'Underline (Ctrl+U)',
39030             text: 'Underline the selected text.',
39031             cls: 'x-html-editor-tip'
39032         },
39033         increasefontsize : {
39034             title: 'Grow Text',
39035             text: 'Increase the font size.',
39036             cls: 'x-html-editor-tip'
39037         },
39038         decreasefontsize : {
39039             title: 'Shrink Text',
39040             text: 'Decrease the font size.',
39041             cls: 'x-html-editor-tip'
39042         },
39043         backcolor : {
39044             title: 'Text Highlight Color',
39045             text: 'Change the background color of the selected text.',
39046             cls: 'x-html-editor-tip'
39047         },
39048         forecolor : {
39049             title: 'Font Color',
39050             text: 'Change the color of the selected text.',
39051             cls: 'x-html-editor-tip'
39052         },
39053         justifyleft : {
39054             title: 'Align Text Left',
39055             text: 'Align text to the left.',
39056             cls: 'x-html-editor-tip'
39057         },
39058         justifycenter : {
39059             title: 'Center Text',
39060             text: 'Center text in the editor.',
39061             cls: 'x-html-editor-tip'
39062         },
39063         justifyright : {
39064             title: 'Align Text Right',
39065             text: 'Align text to the right.',
39066             cls: 'x-html-editor-tip'
39067         },
39068         insertunorderedlist : {
39069             title: 'Bullet List',
39070             text: 'Start a bulleted list.',
39071             cls: 'x-html-editor-tip'
39072         },
39073         insertorderedlist : {
39074             title: 'Numbered List',
39075             text: 'Start a numbered list.',
39076             cls: 'x-html-editor-tip'
39077         },
39078         createlink : {
39079             title: 'Hyperlink',
39080             text: 'Make the selected text a hyperlink.',
39081             cls: 'x-html-editor-tip'
39082         },
39083         sourceedit : {
39084             title: 'Source Edit',
39085             text: 'Switch to source editing mode.',
39086             cls: 'x-html-editor-tip'
39087         }
39088     },
39089     // private
39090     onDestroy : function(){
39091         if(this.rendered){
39092             
39093             this.tb.items.each(function(item){
39094                 if(item.menu){
39095                     item.menu.removeAll();
39096                     if(item.menu.el){
39097                         item.menu.el.destroy();
39098                     }
39099                 }
39100                 item.destroy();
39101             });
39102              
39103         }
39104     },
39105     onFirstFocus: function() {
39106         this.tb.items.each(function(item){
39107            item.enable();
39108         });
39109     }
39110 });
39111
39112
39113
39114
39115 // <script type="text/javascript">
39116 /*
39117  * Based on
39118  * Ext JS Library 1.1.1
39119  * Copyright(c) 2006-2007, Ext JS, LLC.
39120  *  
39121  
39122  */
39123
39124  
39125 /**
39126  * @class Roo.form.HtmlEditor.ToolbarContext
39127  * Context Toolbar
39128  * 
39129  * Usage:
39130  *
39131  new Roo.form.HtmlEditor({
39132     ....
39133     toolbars : [
39134         new Roo.form.HtmlEditor.ToolbarStandard(),
39135         new Roo.form.HtmlEditor.ToolbarContext()
39136         })
39137     }
39138      
39139  * 
39140  * @config : {Object} disable List of elements to disable.. (not done yet.)
39141  * 
39142  * 
39143  */
39144
39145 Roo.form.HtmlEditor.ToolbarContext = function(config)
39146 {
39147     
39148     Roo.apply(this, config);
39149     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39150     // dont call parent... till later.
39151 }
39152 Roo.form.HtmlEditor.ToolbarContext.types = {
39153     'IMG' : {
39154         width : {
39155             title: "Width",
39156             width: 40
39157         },
39158         height:  {
39159             title: "Height",
39160             width: 40
39161         },
39162         align: {
39163             title: "Align",
39164             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39165             width : 80
39166             
39167         },
39168         border: {
39169             title: "Border",
39170             width: 40
39171         },
39172         alt: {
39173             title: "Alt",
39174             width: 120
39175         },
39176         src : {
39177             title: "Src",
39178             width: 220
39179         }
39180         
39181     },
39182     'A' : {
39183         name : {
39184             title: "Name",
39185             width: 50
39186         },
39187         href:  {
39188             title: "Href",
39189             width: 220
39190         } // border?
39191         
39192     },
39193     'TABLE' : {
39194         rows : {
39195             title: "Rows",
39196             width: 20
39197         },
39198         cols : {
39199             title: "Cols",
39200             width: 20
39201         },
39202         width : {
39203             title: "Width",
39204             width: 40
39205         },
39206         height : {
39207             title: "Height",
39208             width: 40
39209         },
39210         border : {
39211             title: "Border",
39212             width: 20
39213         }
39214     },
39215     'TD' : {
39216         width : {
39217             title: "Width",
39218             width: 40
39219         },
39220         height : {
39221             title: "Height",
39222             width: 40
39223         },   
39224         align: {
39225             title: "Align",
39226             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39227             width: 40
39228         },
39229         valign: {
39230             title: "Valign",
39231             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39232             width: 40
39233         },
39234         colspan: {
39235             title: "Colspan",
39236             width: 20
39237             
39238         }
39239     },
39240     'INPUT' : {
39241         name : {
39242             title: "name",
39243             width: 120
39244         },
39245         value : {
39246             title: "Value",
39247             width: 120
39248         },
39249         width : {
39250             title: "Width",
39251             width: 40
39252         }
39253     },
39254     'LABEL' : {
39255         'for' : {
39256             title: "For",
39257             width: 120
39258         }
39259     },
39260     'TEXTAREA' : {
39261           name : {
39262             title: "name",
39263             width: 120
39264         },
39265         rows : {
39266             title: "Rows",
39267             width: 20
39268         },
39269         cols : {
39270             title: "Cols",
39271             width: 20
39272         }
39273     },
39274     'SELECT' : {
39275         name : {
39276             title: "name",
39277             width: 120
39278         },
39279         selectoptions : {
39280             title: "Options",
39281             width: 200
39282         }
39283     },
39284     'BODY' : {
39285         title : {
39286             title: "title",
39287             width: 120,
39288             disabled : true
39289         }
39290     }
39291 };
39292
39293
39294
39295 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39296     
39297     tb: false,
39298     
39299     rendered: false,
39300     
39301     editor : false,
39302     /**
39303      * @cfg {Object} disable  List of toolbar elements to disable
39304          
39305      */
39306     disable : false,
39307     
39308     
39309     
39310     toolbars : false,
39311     
39312     init : function(editor)
39313     {
39314         this.editor = editor;
39315         
39316         
39317         var fid = editor.frameId;
39318         var etb = this;
39319         function btn(id, toggle, handler){
39320             var xid = fid + '-'+ id ;
39321             return {
39322                 id : xid,
39323                 cmd : id,
39324                 cls : 'x-btn-icon x-edit-'+id,
39325                 enableToggle:toggle !== false,
39326                 scope: editor, // was editor...
39327                 handler:handler||editor.relayBtnCmd,
39328                 clickEvent:'mousedown',
39329                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39330                 tabIndex:-1
39331             };
39332         }
39333         // create a new element.
39334         var wdiv = editor.wrap.createChild({
39335                 tag: 'div'
39336             }, editor.wrap.dom.firstChild.nextSibling, true);
39337         
39338         // can we do this more than once??
39339         
39340          // stop form submits
39341       
39342  
39343         // disable everything...
39344         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39345         this.toolbars = {};
39346            
39347         for (var i in  ty) {
39348             this.toolbars[i] = this.buildToolbar(ty[i],i);
39349         }
39350         this.tb = this.toolbars.BODY;
39351         this.tb.el.show();
39352         
39353          
39354         this.rendered = true;
39355         
39356         // the all the btns;
39357         editor.on('editorevent', this.updateToolbar, this);
39358         // other toolbars need to implement this..
39359         //editor.on('editmodechange', this.updateToolbar, this);
39360     },
39361     
39362     
39363     
39364     /**
39365      * Protected method that will not generally be called directly. It triggers
39366      * a toolbar update by reading the markup state of the current selection in the editor.
39367      */
39368     updateToolbar: function(){
39369
39370         if(!this.editor.activated){
39371             this.editor.onFirstFocus();
39372             return;
39373         }
39374
39375         
39376         var ans = this.editor.getAllAncestors();
39377         
39378         // pick
39379         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39380         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39381         sel = sel ? sel : this.editor.doc.body;
39382         sel = sel.tagName.length ? sel : this.editor.doc.body;
39383         var tn = sel.tagName.toUpperCase();
39384         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39385         tn = sel.tagName.toUpperCase();
39386         if (this.tb.name  == tn) {
39387             return; // no change
39388         }
39389         this.tb.el.hide();
39390         ///console.log("show: " + tn);
39391         this.tb =  this.toolbars[tn];
39392         this.tb.el.show();
39393         this.tb.fields.each(function(e) {
39394             e.setValue(sel.getAttribute(e.name));
39395         });
39396         this.tb.selectedNode = sel;
39397         
39398         
39399         Roo.menu.MenuMgr.hideAll();
39400
39401         //this.editorsyncValue();
39402     },
39403    
39404        
39405     // private
39406     onDestroy : function(){
39407         if(this.rendered){
39408             
39409             this.tb.items.each(function(item){
39410                 if(item.menu){
39411                     item.menu.removeAll();
39412                     if(item.menu.el){
39413                         item.menu.el.destroy();
39414                     }
39415                 }
39416                 item.destroy();
39417             });
39418              
39419         }
39420     },
39421     onFirstFocus: function() {
39422         // need to do this for all the toolbars..
39423         this.tb.items.each(function(item){
39424            item.enable();
39425         });
39426     },
39427     buildToolbar: function(tlist, nm)
39428     {
39429         var editor = this.editor;
39430          // create a new element.
39431         var wdiv = editor.wrap.createChild({
39432                 tag: 'div'
39433             }, editor.wrap.dom.firstChild.nextSibling, true);
39434         
39435        
39436         var tb = new Roo.Toolbar(wdiv);
39437         tb.add(nm+ ":&nbsp;");
39438         for (var i in tlist) {
39439             var item = tlist[i];
39440             tb.add(item.title + ":&nbsp;");
39441             if (item.opts) {
39442                 // fixme
39443                 
39444               
39445                 tb.addField( new Roo.form.ComboBox({
39446                     store: new Roo.data.SimpleStore({
39447                         id : 'val',
39448                         fields: ['val'],
39449                         data : item.opts // from states.js
39450                     }),
39451                     name : i,
39452                     displayField:'val',
39453                     typeAhead: false,
39454                     mode: 'local',
39455                     editable : false,
39456                     triggerAction: 'all',
39457                     emptyText:'Select',
39458                     selectOnFocus:true,
39459                     width: item.width ? item.width  : 130,
39460                     listeners : {
39461                         'select': function(c, r, i) {
39462                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39463                         }
39464                     }
39465
39466                 }));
39467                 continue;
39468                     
39469                 
39470                 
39471                 
39472                 
39473                 tb.addField( new Roo.form.TextField({
39474                     name: i,
39475                     width: 100,
39476                     //allowBlank:false,
39477                     value: ''
39478                 }));
39479                 continue;
39480             }
39481             tb.addField( new Roo.form.TextField({
39482                 name: i,
39483                 width: item.width,
39484                 //allowBlank:true,
39485                 value: '',
39486                 listeners: {
39487                     'change' : function(f, nv, ov) {
39488                         tb.selectedNode.setAttribute(f.name, nv);
39489                     }
39490                 }
39491             }));
39492              
39493         }
39494         tb.el.on('click', function(e){
39495             e.preventDefault(); // what does this do?
39496         });
39497         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39498         tb.el.hide();
39499         tb.name = nm;
39500         // dont need to disable them... as they will get hidden
39501         return tb;
39502          
39503         
39504     }
39505     
39506     
39507     
39508     
39509 });
39510
39511
39512
39513
39514
39515 /*
39516  * Based on:
39517  * Ext JS Library 1.1.1
39518  * Copyright(c) 2006-2007, Ext JS, LLC.
39519  *
39520  * Originally Released Under LGPL - original licence link has changed is not relivant.
39521  *
39522  * Fork - LGPL
39523  * <script type="text/javascript">
39524  */
39525  
39526 /**
39527  * @class Roo.form.BasicForm
39528  * @extends Roo.util.Observable
39529  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39530  * @constructor
39531  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39532  * @param {Object} config Configuration options
39533  */
39534 Roo.form.BasicForm = function(el, config){
39535     Roo.apply(this, config);
39536     /*
39537      * The Roo.form.Field items in this form.
39538      * @type MixedCollection
39539      */
39540     this.items = new Roo.util.MixedCollection(false, function(o){
39541         return o.id || (o.id = Roo.id());
39542     });
39543     this.addEvents({
39544         /**
39545          * @event beforeaction
39546          * Fires before any action is performed. Return false to cancel the action.
39547          * @param {Form} this
39548          * @param {Action} action The action to be performed
39549          */
39550         beforeaction: true,
39551         /**
39552          * @event actionfailed
39553          * Fires when an action fails.
39554          * @param {Form} this
39555          * @param {Action} action The action that failed
39556          */
39557         actionfailed : true,
39558         /**
39559          * @event actioncomplete
39560          * Fires when an action is completed.
39561          * @param {Form} this
39562          * @param {Action} action The action that completed
39563          */
39564         actioncomplete : true
39565     });
39566     if(el){
39567         this.initEl(el);
39568     }
39569     Roo.form.BasicForm.superclass.constructor.call(this);
39570 };
39571
39572 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39573     /**
39574      * @cfg {String} method
39575      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39576      */
39577     /**
39578      * @cfg {DataReader} reader
39579      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39580      * This is optional as there is built-in support for processing JSON.
39581      */
39582     /**
39583      * @cfg {DataReader} errorReader
39584      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39585      * This is completely optional as there is built-in support for processing JSON.
39586      */
39587     /**
39588      * @cfg {String} url
39589      * The URL to use for form actions if one isn't supplied in the action options.
39590      */
39591     /**
39592      * @cfg {Boolean} fileUpload
39593      * Set to true if this form is a file upload.
39594      */
39595     /**
39596      * @cfg {Object} baseParams
39597      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39598      */
39599     /**
39600      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39601      */
39602     timeout: 30,
39603
39604     // private
39605     activeAction : null,
39606
39607     /**
39608      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39609      * or setValues() data instead of when the form was first created.
39610      */
39611     trackResetOnLoad : false,
39612
39613     /**
39614      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39615      * element by passing it or its id or mask the form itself by passing in true.
39616      * @type Mixed
39617      */
39618     waitMsgTarget : undefined,
39619
39620     // private
39621     initEl : function(el){
39622         this.el = Roo.get(el);
39623         this.id = this.el.id || Roo.id();
39624         this.el.on('submit', this.onSubmit, this);
39625         this.el.addClass('x-form');
39626     },
39627
39628     // private
39629     onSubmit : function(e){
39630         e.stopEvent();
39631     },
39632
39633     /**
39634      * Returns true if client-side validation on the form is successful.
39635      * @return Boolean
39636      */
39637     isValid : function(){
39638         var valid = true;
39639         this.items.each(function(f){
39640            if(!f.validate()){
39641                valid = false;
39642            }
39643         });
39644         return valid;
39645     },
39646
39647     /**
39648      * Returns true if any fields in this form have changed since their original load.
39649      * @return Boolean
39650      */
39651     isDirty : function(){
39652         var dirty = false;
39653         this.items.each(function(f){
39654            if(f.isDirty()){
39655                dirty = true;
39656                return false;
39657            }
39658         });
39659         return dirty;
39660     },
39661
39662     /**
39663      * Performs a predefined action (submit or load) or custom actions you define on this form.
39664      * @param {String} actionName The name of the action type
39665      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39666      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39667      * accept other config options):
39668      * <pre>
39669 Property          Type             Description
39670 ----------------  ---------------  ----------------------------------------------------------------------------------
39671 url               String           The url for the action (defaults to the form's url)
39672 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39673 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39674 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39675                                    validate the form on the client (defaults to false)
39676      * </pre>
39677      * @return {BasicForm} this
39678      */
39679     doAction : function(action, options){
39680         if(typeof action == 'string'){
39681             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39682         }
39683         if(this.fireEvent('beforeaction', this, action) !== false){
39684             this.beforeAction(action);
39685             action.run.defer(100, action);
39686         }
39687         return this;
39688     },
39689
39690     /**
39691      * Shortcut to do a submit action.
39692      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39693      * @return {BasicForm} this
39694      */
39695     submit : function(options){
39696         this.doAction('submit', options);
39697         return this;
39698     },
39699
39700     /**
39701      * Shortcut to do a load action.
39702      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39703      * @return {BasicForm} this
39704      */
39705     load : function(options){
39706         this.doAction('load', options);
39707         return this;
39708     },
39709
39710     /**
39711      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39712      * @param {Record} record The record to edit
39713      * @return {BasicForm} this
39714      */
39715     updateRecord : function(record){
39716         record.beginEdit();
39717         var fs = record.fields;
39718         fs.each(function(f){
39719             var field = this.findField(f.name);
39720             if(field){
39721                 record.set(f.name, field.getValue());
39722             }
39723         }, this);
39724         record.endEdit();
39725         return this;
39726     },
39727
39728     /**
39729      * Loads an Roo.data.Record into this form.
39730      * @param {Record} record The record to load
39731      * @return {BasicForm} this
39732      */
39733     loadRecord : function(record){
39734         this.setValues(record.data);
39735         return this;
39736     },
39737
39738     // private
39739     beforeAction : function(action){
39740         var o = action.options;
39741         if(o.waitMsg){
39742             if(this.waitMsgTarget === true){
39743                 this.el.mask(o.waitMsg, 'x-mask-loading');
39744             }else if(this.waitMsgTarget){
39745                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39746                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39747             }else{
39748                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39749             }
39750         }
39751     },
39752
39753     // private
39754     afterAction : function(action, success){
39755         this.activeAction = null;
39756         var o = action.options;
39757         if(o.waitMsg){
39758             if(this.waitMsgTarget === true){
39759                 this.el.unmask();
39760             }else if(this.waitMsgTarget){
39761                 this.waitMsgTarget.unmask();
39762             }else{
39763                 Roo.MessageBox.updateProgress(1);
39764                 Roo.MessageBox.hide();
39765             }
39766         }
39767         if(success){
39768             if(o.reset){
39769                 this.reset();
39770             }
39771             Roo.callback(o.success, o.scope, [this, action]);
39772             this.fireEvent('actioncomplete', this, action);
39773         }else{
39774             Roo.callback(o.failure, o.scope, [this, action]);
39775             this.fireEvent('actionfailed', this, action);
39776         }
39777     },
39778
39779     /**
39780      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
39781      * @param {String} id The value to search for
39782      * @return Field
39783      */
39784     findField : function(id){
39785         var field = this.items.get(id);
39786         if(!field){
39787             this.items.each(function(f){
39788                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
39789                     field = f;
39790                     return false;
39791                 }
39792             });
39793         }
39794         return field || null;
39795     },
39796
39797
39798     /**
39799      * Mark fields in this form invalid in bulk.
39800      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
39801      * @return {BasicForm} this
39802      */
39803     markInvalid : function(errors){
39804         if(errors instanceof Array){
39805             for(var i = 0, len = errors.length; i < len; i++){
39806                 var fieldError = errors[i];
39807                 var f = this.findField(fieldError.id);
39808                 if(f){
39809                     f.markInvalid(fieldError.msg);
39810                 }
39811             }
39812         }else{
39813             var field, id;
39814             for(id in errors){
39815                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
39816                     field.markInvalid(errors[id]);
39817                 }
39818             }
39819         }
39820         return this;
39821     },
39822
39823     /**
39824      * Set values for fields in this form in bulk.
39825      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
39826      * @return {BasicForm} this
39827      */
39828     setValues : function(values){
39829         if(values instanceof Array){ // array of objects
39830             for(var i = 0, len = values.length; i < len; i++){
39831                 var v = values[i];
39832                 var f = this.findField(v.id);
39833                 if(f){
39834                     f.setValue(v.value);
39835                     if(this.trackResetOnLoad){
39836                         f.originalValue = f.getValue();
39837                     }
39838                 }
39839             }
39840         }else{ // object hash
39841             var field, id;
39842             for(id in values){
39843                 if(typeof values[id] != 'function' && (field = this.findField(id))){
39844                     
39845                     if (field.setFromData && 
39846                         field.valueField && 
39847                         field.displayField &&
39848                         // combos' with local stores can 
39849                         // be queried via setValue()
39850                         // to set their value..
39851                         (field.store && !field.store.isLocal)
39852                         ) {
39853                         // it's a combo
39854                         var sd = { };
39855                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
39856                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
39857                         field.setFromData(sd);
39858                         
39859                     } else {
39860                         field.setValue(values[id]);
39861                     }
39862                     
39863                     
39864                     if(this.trackResetOnLoad){
39865                         field.originalValue = field.getValue();
39866                     }
39867                 }
39868             }
39869         }
39870         return this;
39871     },
39872
39873     /**
39874      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
39875      * they are returned as an array.
39876      * @param {Boolean} asString
39877      * @return {Object}
39878      */
39879     getValues : function(asString){
39880         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
39881         if(asString === true){
39882             return fs;
39883         }
39884         return Roo.urlDecode(fs);
39885     },
39886
39887     /**
39888      * Clears all invalid messages in this form.
39889      * @return {BasicForm} this
39890      */
39891     clearInvalid : function(){
39892         this.items.each(function(f){
39893            f.clearInvalid();
39894         });
39895         return this;
39896     },
39897
39898     /**
39899      * Resets this form.
39900      * @return {BasicForm} this
39901      */
39902     reset : function(){
39903         this.items.each(function(f){
39904             f.reset();
39905         });
39906         return this;
39907     },
39908
39909     /**
39910      * Add Roo.form components to this form.
39911      * @param {Field} field1
39912      * @param {Field} field2 (optional)
39913      * @param {Field} etc (optional)
39914      * @return {BasicForm} this
39915      */
39916     add : function(){
39917         this.items.addAll(Array.prototype.slice.call(arguments, 0));
39918         return this;
39919     },
39920
39921
39922     /**
39923      * Removes a field from the items collection (does NOT remove its markup).
39924      * @param {Field} field
39925      * @return {BasicForm} this
39926      */
39927     remove : function(field){
39928         this.items.remove(field);
39929         return this;
39930     },
39931
39932     /**
39933      * Looks at the fields in this form, checks them for an id attribute,
39934      * and calls applyTo on the existing dom element with that id.
39935      * @return {BasicForm} this
39936      */
39937     render : function(){
39938         this.items.each(function(f){
39939             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
39940                 f.applyTo(f.id);
39941             }
39942         });
39943         return this;
39944     },
39945
39946     /**
39947      * Calls {@link Ext#apply} for all fields in this form with the passed object.
39948      * @param {Object} values
39949      * @return {BasicForm} this
39950      */
39951     applyToFields : function(o){
39952         this.items.each(function(f){
39953            Roo.apply(f, o);
39954         });
39955         return this;
39956     },
39957
39958     /**
39959      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
39960      * @param {Object} values
39961      * @return {BasicForm} this
39962      */
39963     applyIfToFields : function(o){
39964         this.items.each(function(f){
39965            Roo.applyIf(f, o);
39966         });
39967         return this;
39968     }
39969 });
39970
39971 // back compat
39972 Roo.BasicForm = Roo.form.BasicForm;/*
39973  * Based on:
39974  * Ext JS Library 1.1.1
39975  * Copyright(c) 2006-2007, Ext JS, LLC.
39976  *
39977  * Originally Released Under LGPL - original licence link has changed is not relivant.
39978  *
39979  * Fork - LGPL
39980  * <script type="text/javascript">
39981  */
39982
39983 /**
39984  * @class Roo.form.Form
39985  * @extends Roo.form.BasicForm
39986  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
39987  * @constructor
39988  * @param {Object} config Configuration options
39989  */
39990 Roo.form.Form = function(config){
39991     var xitems =  [];
39992     if (config.items) {
39993         xitems = config.items;
39994         delete config.items;
39995     }
39996     
39997     
39998     Roo.form.Form.superclass.constructor.call(this, null, config);
39999     this.url = this.url || this.action;
40000     if(!this.root){
40001         this.root = new Roo.form.Layout(Roo.applyIf({
40002             id: Roo.id()
40003         }, config));
40004     }
40005     this.active = this.root;
40006     /**
40007      * Array of all the buttons that have been added to this form via {@link addButton}
40008      * @type Array
40009      */
40010     this.buttons = [];
40011     this.allItems = [];
40012     this.addEvents({
40013         /**
40014          * @event clientvalidation
40015          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40016          * @param {Form} this
40017          * @param {Boolean} valid true if the form has passed client-side validation
40018          */
40019         clientvalidation: true,
40020         /**
40021          * @event rendered
40022          * Fires when the form is rendered
40023          * @param {Roo.form.Form} form
40024          */
40025         rendered : true
40026     });
40027     
40028     Roo.each(xitems, this.addxtype, this);
40029     
40030     
40031     
40032 };
40033
40034 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40035     /**
40036      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40037      */
40038     /**
40039      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40040      */
40041     /**
40042      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40043      */
40044     buttonAlign:'center',
40045
40046     /**
40047      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40048      */
40049     minButtonWidth:75,
40050
40051     /**
40052      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40053      * This property cascades to child containers if not set.
40054      */
40055     labelAlign:'left',
40056
40057     /**
40058      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40059      * fires a looping event with that state. This is required to bind buttons to the valid
40060      * state using the config value formBind:true on the button.
40061      */
40062     monitorValid : false,
40063
40064     /**
40065      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40066      */
40067     monitorPoll : 200,
40068
40069     /**
40070      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40071      * fields are added and the column is closed. If no fields are passed the column remains open
40072      * until end() is called.
40073      * @param {Object} config The config to pass to the column
40074      * @param {Field} field1 (optional)
40075      * @param {Field} field2 (optional)
40076      * @param {Field} etc (optional)
40077      * @return Column The column container object
40078      */
40079     column : function(c){
40080         var col = new Roo.form.Column(c);
40081         this.start(col);
40082         if(arguments.length > 1){ // duplicate code required because of Opera
40083             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40084             this.end();
40085         }
40086         return col;
40087     },
40088
40089     /**
40090      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40091      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40092      * until end() is called.
40093      * @param {Object} config The config to pass to the fieldset
40094      * @param {Field} field1 (optional)
40095      * @param {Field} field2 (optional)
40096      * @param {Field} etc (optional)
40097      * @return FieldSet The fieldset container object
40098      */
40099     fieldset : function(c){
40100         var fs = new Roo.form.FieldSet(c);
40101         this.start(fs);
40102         if(arguments.length > 1){ // duplicate code required because of Opera
40103             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40104             this.end();
40105         }
40106         return fs;
40107     },
40108
40109     /**
40110      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40111      * fields are added and the container is closed. If no fields are passed the container remains open
40112      * until end() is called.
40113      * @param {Object} config The config to pass to the Layout
40114      * @param {Field} field1 (optional)
40115      * @param {Field} field2 (optional)
40116      * @param {Field} etc (optional)
40117      * @return Layout The container object
40118      */
40119     container : function(c){
40120         var l = new Roo.form.Layout(c);
40121         this.start(l);
40122         if(arguments.length > 1){ // duplicate code required because of Opera
40123             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40124             this.end();
40125         }
40126         return l;
40127     },
40128
40129     /**
40130      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40131      * @param {Object} container A Roo.form.Layout or subclass of Layout
40132      * @return {Form} this
40133      */
40134     start : function(c){
40135         // cascade label info
40136         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40137         this.active.stack.push(c);
40138         c.ownerCt = this.active;
40139         this.active = c;
40140         return this;
40141     },
40142
40143     /**
40144      * Closes the current open container
40145      * @return {Form} this
40146      */
40147     end : function(){
40148         if(this.active == this.root){
40149             return this;
40150         }
40151         this.active = this.active.ownerCt;
40152         return this;
40153     },
40154
40155     /**
40156      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40157      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40158      * as the label of the field.
40159      * @param {Field} field1
40160      * @param {Field} field2 (optional)
40161      * @param {Field} etc. (optional)
40162      * @return {Form} this
40163      */
40164     add : function(){
40165         this.active.stack.push.apply(this.active.stack, arguments);
40166         this.allItems.push.apply(this.allItems,arguments);
40167         var r = [];
40168         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40169             if(a[i].isFormField){
40170                 r.push(a[i]);
40171             }
40172         }
40173         if(r.length > 0){
40174             Roo.form.Form.superclass.add.apply(this, r);
40175         }
40176         return this;
40177     },
40178      /**
40179      * Find any element that has been added to a form, using it's ID or name
40180      * This can include framesets, columns etc. along with regular fields..
40181      * @param {String} id - id or name to find.
40182      
40183      * @return {Element} e - or false if nothing found.
40184      */
40185     findbyId : function(id)
40186     {
40187         var ret = false;
40188         if (!id) {
40189             return ret;
40190         }
40191         Ext.each(this.allItems, function(f){
40192             if (f.id == id || f.name == id ){
40193                 ret = f;
40194                 return false;
40195             }
40196         });
40197         return ret;
40198     },
40199
40200     
40201     
40202     /**
40203      * Render this form into the passed container. This should only be called once!
40204      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40205      * @return {Form} this
40206      */
40207     render : function(ct){
40208         ct = Roo.get(ct);
40209         var o = this.autoCreate || {
40210             tag: 'form',
40211             method : this.method || 'POST',
40212             id : this.id || Roo.id()
40213         };
40214         this.initEl(ct.createChild(o));
40215
40216         this.root.render(this.el);
40217
40218         this.items.each(function(f){
40219             f.render('x-form-el-'+f.id);
40220         });
40221
40222         if(this.buttons.length > 0){
40223             // tables are required to maintain order and for correct IE layout
40224             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40225                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40226                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40227             }}, null, true);
40228             var tr = tb.getElementsByTagName('tr')[0];
40229             for(var i = 0, len = this.buttons.length; i < len; i++) {
40230                 var b = this.buttons[i];
40231                 var td = document.createElement('td');
40232                 td.className = 'x-form-btn-td';
40233                 b.render(tr.appendChild(td));
40234             }
40235         }
40236         if(this.monitorValid){ // initialize after render
40237             this.startMonitoring();
40238         }
40239         this.fireEvent('rendered', this);
40240         return this;
40241     },
40242
40243     /**
40244      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40245      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40246      * object or a valid Roo.DomHelper element config
40247      * @param {Function} handler The function called when the button is clicked
40248      * @param {Object} scope (optional) The scope of the handler function
40249      * @return {Roo.Button}
40250      */
40251     addButton : function(config, handler, scope){
40252         var bc = {
40253             handler: handler,
40254             scope: scope,
40255             minWidth: this.minButtonWidth,
40256             hideParent:true
40257         };
40258         if(typeof config == "string"){
40259             bc.text = config;
40260         }else{
40261             Roo.apply(bc, config);
40262         }
40263         var btn = new Roo.Button(null, bc);
40264         this.buttons.push(btn);
40265         return btn;
40266     },
40267
40268      /**
40269      * Adds a series of form elements (using the xtype property as the factory method.
40270      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40271      * @param {Object} config 
40272      */
40273     
40274     addxtype : function()
40275     {
40276         var ar = Array.prototype.slice.call(arguments, 0);
40277         var ret = false;
40278         for(var i = 0; i < ar.length; i++) {
40279             if (!ar[i]) {
40280                 continue; // skip -- if this happends something invalid got sent, we 
40281                 // should ignore it, as basically that interface element will not show up
40282                 // and that should be pretty obvious!!
40283             }
40284             
40285             if (Roo.form[ar[i].xtype]) {
40286                 ar[i].form = this;
40287                 var fe = Roo.factory(ar[i], Roo.form);
40288                 if (!ret) {
40289                     ret = fe;
40290                 }
40291                 fe.form = this;
40292                 if (fe.store) {
40293                     fe.store.form = this;
40294                 }
40295                 if (fe.isLayout) {  
40296                          
40297                     this.start(fe);
40298                     this.allItems.push(fe);
40299                     if (fe.items && fe.addxtype) {
40300                         fe.addxtype.apply(fe, fe.items);
40301                         delete fe.items;
40302                     }
40303                      this.end();
40304                     continue;
40305                 }
40306                 
40307                 
40308                  
40309                 this.add(fe);
40310               //  console.log('adding ' + ar[i].xtype);
40311             }
40312             if (ar[i].xtype == 'Button') {  
40313                 //console.log('adding button');
40314                 //console.log(ar[i]);
40315                 this.addButton(ar[i]);
40316                 this.allItems.push(fe);
40317                 continue;
40318             }
40319             
40320             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40321                 alert('end is not supported on xtype any more, use items');
40322             //    this.end();
40323             //    //console.log('adding end');
40324             }
40325             
40326         }
40327         return ret;
40328     },
40329     
40330     /**
40331      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40332      * option "monitorValid"
40333      */
40334     startMonitoring : function(){
40335         if(!this.bound){
40336             this.bound = true;
40337             Roo.TaskMgr.start({
40338                 run : this.bindHandler,
40339                 interval : this.monitorPoll || 200,
40340                 scope: this
40341             });
40342         }
40343     },
40344
40345     /**
40346      * Stops monitoring of the valid state of this form
40347      */
40348     stopMonitoring : function(){
40349         this.bound = false;
40350     },
40351
40352     // private
40353     bindHandler : function(){
40354         if(!this.bound){
40355             return false; // stops binding
40356         }
40357         var valid = true;
40358         this.items.each(function(f){
40359             if(!f.isValid(true)){
40360                 valid = false;
40361                 return false;
40362             }
40363         });
40364         for(var i = 0, len = this.buttons.length; i < len; i++){
40365             var btn = this.buttons[i];
40366             if(btn.formBind === true && btn.disabled === valid){
40367                 btn.setDisabled(!valid);
40368             }
40369         }
40370         this.fireEvent('clientvalidation', this, valid);
40371     }
40372     
40373     
40374     
40375     
40376     
40377     
40378     
40379     
40380 });
40381
40382
40383 // back compat
40384 Roo.Form = Roo.form.Form;
40385 /*
40386  * Based on:
40387  * Ext JS Library 1.1.1
40388  * Copyright(c) 2006-2007, Ext JS, LLC.
40389  *
40390  * Originally Released Under LGPL - original licence link has changed is not relivant.
40391  *
40392  * Fork - LGPL
40393  * <script type="text/javascript">
40394  */
40395  
40396  /**
40397  * @class Roo.form.Action
40398  * Internal Class used to handle form actions
40399  * @constructor
40400  * @param {Roo.form.BasicForm} el The form element or its id
40401  * @param {Object} config Configuration options
40402  */
40403  
40404  
40405 // define the action interface
40406 Roo.form.Action = function(form, options){
40407     this.form = form;
40408     this.options = options || {};
40409 };
40410 /**
40411  * Client Validation Failed
40412  * @const 
40413  */
40414 Roo.form.Action.CLIENT_INVALID = 'client';
40415 /**
40416  * Server Validation Failed
40417  * @const 
40418  */
40419  Roo.form.Action.SERVER_INVALID = 'server';
40420  /**
40421  * Connect to Server Failed
40422  * @const 
40423  */
40424 Roo.form.Action.CONNECT_FAILURE = 'connect';
40425 /**
40426  * Reading Data from Server Failed
40427  * @const 
40428  */
40429 Roo.form.Action.LOAD_FAILURE = 'load';
40430
40431 Roo.form.Action.prototype = {
40432     type : 'default',
40433     failureType : undefined,
40434     response : undefined,
40435     result : undefined,
40436
40437     // interface method
40438     run : function(options){
40439
40440     },
40441
40442     // interface method
40443     success : function(response){
40444
40445     },
40446
40447     // interface method
40448     handleResponse : function(response){
40449
40450     },
40451
40452     // default connection failure
40453     failure : function(response){
40454         this.response = response;
40455         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40456         this.form.afterAction(this, false);
40457     },
40458
40459     processResponse : function(response){
40460         this.response = response;
40461         if(!response.responseText){
40462             return true;
40463         }
40464         this.result = this.handleResponse(response);
40465         return this.result;
40466     },
40467
40468     // utility functions used internally
40469     getUrl : function(appendParams){
40470         var url = this.options.url || this.form.url || this.form.el.dom.action;
40471         if(appendParams){
40472             var p = this.getParams();
40473             if(p){
40474                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40475             }
40476         }
40477         return url;
40478     },
40479
40480     getMethod : function(){
40481         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40482     },
40483
40484     getParams : function(){
40485         var bp = this.form.baseParams;
40486         var p = this.options.params;
40487         if(p){
40488             if(typeof p == "object"){
40489                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40490             }else if(typeof p == 'string' && bp){
40491                 p += '&' + Roo.urlEncode(bp);
40492             }
40493         }else if(bp){
40494             p = Roo.urlEncode(bp);
40495         }
40496         return p;
40497     },
40498
40499     createCallback : function(){
40500         return {
40501             success: this.success,
40502             failure: this.failure,
40503             scope: this,
40504             timeout: (this.form.timeout*1000),
40505             upload: this.form.fileUpload ? this.success : undefined
40506         };
40507     }
40508 };
40509
40510 Roo.form.Action.Submit = function(form, options){
40511     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40512 };
40513
40514 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40515     type : 'submit',
40516
40517     run : function(){
40518         var o = this.options;
40519         var method = this.getMethod();
40520         var isPost = method == 'POST';
40521         if(o.clientValidation === false || this.form.isValid()){
40522             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40523                 form:this.form.el.dom,
40524                 url:this.getUrl(!isPost),
40525                 method: method,
40526                 params:isPost ? this.getParams() : null,
40527                 isUpload: this.form.fileUpload
40528             }));
40529
40530         }else if (o.clientValidation !== false){ // client validation failed
40531             this.failureType = Roo.form.Action.CLIENT_INVALID;
40532             this.form.afterAction(this, false);
40533         }
40534     },
40535
40536     success : function(response){
40537         var result = this.processResponse(response);
40538         if(result === true || result.success){
40539             this.form.afterAction(this, true);
40540             return;
40541         }
40542         if(result.errors){
40543             this.form.markInvalid(result.errors);
40544             this.failureType = Roo.form.Action.SERVER_INVALID;
40545         }
40546         this.form.afterAction(this, false);
40547     },
40548
40549     handleResponse : function(response){
40550         if(this.form.errorReader){
40551             var rs = this.form.errorReader.read(response);
40552             var errors = [];
40553             if(rs.records){
40554                 for(var i = 0, len = rs.records.length; i < len; i++) {
40555                     var r = rs.records[i];
40556                     errors[i] = r.data;
40557                 }
40558             }
40559             if(errors.length < 1){
40560                 errors = null;
40561             }
40562             return {
40563                 success : rs.success,
40564                 errors : errors
40565             };
40566         }
40567         var ret = false;
40568         try {
40569             ret = Roo.decode(response.responseText);
40570         } catch (e) {
40571             ret = {
40572                 success: false,
40573                 errorMsg: "Failed to read server message: " + response.responseText,
40574                 errors : []
40575             };
40576         }
40577         return ret;
40578         
40579     }
40580 });
40581
40582
40583 Roo.form.Action.Load = function(form, options){
40584     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40585     this.reader = this.form.reader;
40586 };
40587
40588 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40589     type : 'load',
40590
40591     run : function(){
40592         Roo.Ajax.request(Roo.apply(
40593                 this.createCallback(), {
40594                     method:this.getMethod(),
40595                     url:this.getUrl(false),
40596                     params:this.getParams()
40597         }));
40598     },
40599
40600     success : function(response){
40601         var result = this.processResponse(response);
40602         if(result === true || !result.success || !result.data){
40603             this.failureType = Roo.form.Action.LOAD_FAILURE;
40604             this.form.afterAction(this, false);
40605             return;
40606         }
40607         this.form.clearInvalid();
40608         this.form.setValues(result.data);
40609         this.form.afterAction(this, true);
40610     },
40611
40612     handleResponse : function(response){
40613         if(this.form.reader){
40614             var rs = this.form.reader.read(response);
40615             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40616             return {
40617                 success : rs.success,
40618                 data : data
40619             };
40620         }
40621         return Roo.decode(response.responseText);
40622     }
40623 });
40624
40625 Roo.form.Action.ACTION_TYPES = {
40626     'load' : Roo.form.Action.Load,
40627     'submit' : Roo.form.Action.Submit
40628 };/*
40629  * Based on:
40630  * Ext JS Library 1.1.1
40631  * Copyright(c) 2006-2007, Ext JS, LLC.
40632  *
40633  * Originally Released Under LGPL - original licence link has changed is not relivant.
40634  *
40635  * Fork - LGPL
40636  * <script type="text/javascript">
40637  */
40638  
40639 /**
40640  * @class Roo.form.Layout
40641  * @extends Roo.Component
40642  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40643  * @constructor
40644  * @param {Object} config Configuration options
40645  */
40646 Roo.form.Layout = function(config){
40647     var xitems = [];
40648     if (config.items) {
40649         xitems = config.items;
40650         delete config.items;
40651     }
40652     Roo.form.Layout.superclass.constructor.call(this, config);
40653     this.stack = [];
40654     Roo.each(xitems, this.addxtype, this);
40655      
40656 };
40657
40658 Roo.extend(Roo.form.Layout, Roo.Component, {
40659     /**
40660      * @cfg {String/Object} autoCreate
40661      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40662      */
40663     /**
40664      * @cfg {String/Object/Function} style
40665      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40666      * a function which returns such a specification.
40667      */
40668     /**
40669      * @cfg {String} labelAlign
40670      * Valid values are "left," "top" and "right" (defaults to "left")
40671      */
40672     /**
40673      * @cfg {Number} labelWidth
40674      * Fixed width in pixels of all field labels (defaults to undefined)
40675      */
40676     /**
40677      * @cfg {Boolean} clear
40678      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40679      */
40680     clear : true,
40681     /**
40682      * @cfg {String} labelSeparator
40683      * The separator to use after field labels (defaults to ':')
40684      */
40685     labelSeparator : ':',
40686     /**
40687      * @cfg {Boolean} hideLabels
40688      * True to suppress the display of field labels in this layout (defaults to false)
40689      */
40690     hideLabels : false,
40691
40692     // private
40693     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40694     
40695     isLayout : true,
40696     
40697     // private
40698     onRender : function(ct, position){
40699         if(this.el){ // from markup
40700             this.el = Roo.get(this.el);
40701         }else {  // generate
40702             var cfg = this.getAutoCreate();
40703             this.el = ct.createChild(cfg, position);
40704         }
40705         if(this.style){
40706             this.el.applyStyles(this.style);
40707         }
40708         if(this.labelAlign){
40709             this.el.addClass('x-form-label-'+this.labelAlign);
40710         }
40711         if(this.hideLabels){
40712             this.labelStyle = "display:none";
40713             this.elementStyle = "padding-left:0;";
40714         }else{
40715             if(typeof this.labelWidth == 'number'){
40716                 this.labelStyle = "width:"+this.labelWidth+"px;";
40717                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
40718             }
40719             if(this.labelAlign == 'top'){
40720                 this.labelStyle = "width:auto;";
40721                 this.elementStyle = "padding-left:0;";
40722             }
40723         }
40724         var stack = this.stack;
40725         var slen = stack.length;
40726         if(slen > 0){
40727             if(!this.fieldTpl){
40728                 var t = new Roo.Template(
40729                     '<div class="x-form-item {5}">',
40730                         '<label for="{0}" style="{2}">{1}{4}</label>',
40731                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40732                         '</div>',
40733                     '</div><div class="x-form-clear-left"></div>'
40734                 );
40735                 t.disableFormats = true;
40736                 t.compile();
40737                 Roo.form.Layout.prototype.fieldTpl = t;
40738             }
40739             for(var i = 0; i < slen; i++) {
40740                 if(stack[i].isFormField){
40741                     this.renderField(stack[i]);
40742                 }else{
40743                     this.renderComponent(stack[i]);
40744                 }
40745             }
40746         }
40747         if(this.clear){
40748             this.el.createChild({cls:'x-form-clear'});
40749         }
40750     },
40751
40752     // private
40753     renderField : function(f){
40754         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
40755                f.id, //0
40756                f.fieldLabel, //1
40757                f.labelStyle||this.labelStyle||'', //2
40758                this.elementStyle||'', //3
40759                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
40760                f.itemCls||this.itemCls||''  //5
40761        ], true).getPrevSibling());
40762     },
40763
40764     // private
40765     renderComponent : function(c){
40766         c.render(c.isLayout ? this.el : this.el.createChild());    
40767     },
40768     /**
40769      * Adds a object form elements (using the xtype property as the factory method.)
40770      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
40771      * @param {Object} config 
40772      */
40773     addxtype : function(o)
40774     {
40775         // create the lement.
40776         o.form = this.form;
40777         var fe = Roo.factory(o, Roo.form);
40778         this.form.allItems.push(fe);
40779         this.stack.push(fe);
40780         
40781         if (fe.isFormField) {
40782             this.form.items.add(fe);
40783         }
40784          
40785         return fe;
40786     }
40787 });
40788
40789 /**
40790  * @class Roo.form.Column
40791  * @extends Roo.form.Layout
40792  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
40793  * @constructor
40794  * @param {Object} config Configuration options
40795  */
40796 Roo.form.Column = function(config){
40797     Roo.form.Column.superclass.constructor.call(this, config);
40798 };
40799
40800 Roo.extend(Roo.form.Column, Roo.form.Layout, {
40801     /**
40802      * @cfg {Number/String} width
40803      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40804      */
40805     /**
40806      * @cfg {String/Object} autoCreate
40807      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
40808      */
40809
40810     // private
40811     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
40812
40813     // private
40814     onRender : function(ct, position){
40815         Roo.form.Column.superclass.onRender.call(this, ct, position);
40816         if(this.width){
40817             this.el.setWidth(this.width);
40818         }
40819     }
40820 });
40821
40822
40823 /**
40824  * @class Roo.form.Row
40825  * @extends Roo.form.Layout
40826  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
40827  * @constructor
40828  * @param {Object} config Configuration options
40829  */
40830
40831  
40832 Roo.form.Row = function(config){
40833     Roo.form.Row.superclass.constructor.call(this, config);
40834 };
40835  
40836 Roo.extend(Roo.form.Row, Roo.form.Layout, {
40837       /**
40838      * @cfg {Number/String} width
40839      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40840      */
40841     /**
40842      * @cfg {Number/String} height
40843      * The fixed height of the column in pixels or CSS value (defaults to "auto")
40844      */
40845     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
40846     
40847     padWidth : 20,
40848     // private
40849     onRender : function(ct, position){
40850         //console.log('row render');
40851         if(!this.rowTpl){
40852             var t = new Roo.Template(
40853                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
40854                     '<label for="{0}" style="{2}">{1}{4}</label>',
40855                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40856                     '</div>',
40857                 '</div>'
40858             );
40859             t.disableFormats = true;
40860             t.compile();
40861             Roo.form.Layout.prototype.rowTpl = t;
40862         }
40863         this.fieldTpl = this.rowTpl;
40864         
40865         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
40866         var labelWidth = 100;
40867         
40868         if ((this.labelAlign != 'top')) {
40869             if (typeof this.labelWidth == 'number') {
40870                 labelWidth = this.labelWidth
40871             }
40872             this.padWidth =  20 + labelWidth;
40873             
40874         }
40875         
40876         Roo.form.Column.superclass.onRender.call(this, ct, position);
40877         if(this.width){
40878             this.el.setWidth(this.width);
40879         }
40880         if(this.height){
40881             this.el.setHeight(this.height);
40882         }
40883     },
40884     
40885     // private
40886     renderField : function(f){
40887         f.fieldEl = this.fieldTpl.append(this.el, [
40888                f.id, f.fieldLabel,
40889                f.labelStyle||this.labelStyle||'',
40890                this.elementStyle||'',
40891                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
40892                f.itemCls||this.itemCls||'',
40893                f.width ? f.width + this.padWidth : 160 + this.padWidth
40894        ],true);
40895     }
40896 });
40897  
40898
40899 /**
40900  * @class Roo.form.FieldSet
40901  * @extends Roo.form.Layout
40902  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
40903  * @constructor
40904  * @param {Object} config Configuration options
40905  */
40906 Roo.form.FieldSet = function(config){
40907     Roo.form.FieldSet.superclass.constructor.call(this, config);
40908 };
40909
40910 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
40911     /**
40912      * @cfg {String} legend
40913      * The text to display as the legend for the FieldSet (defaults to '')
40914      */
40915     /**
40916      * @cfg {String/Object} autoCreate
40917      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
40918      */
40919
40920     // private
40921     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
40922
40923     // private
40924     onRender : function(ct, position){
40925         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
40926         if(this.legend){
40927             this.setLegend(this.legend);
40928         }
40929     },
40930
40931     // private
40932     setLegend : function(text){
40933         if(this.rendered){
40934             this.el.child('legend').update(text);
40935         }
40936     }
40937 });/*
40938  * Based on:
40939  * Ext JS Library 1.1.1
40940  * Copyright(c) 2006-2007, Ext JS, LLC.
40941  *
40942  * Originally Released Under LGPL - original licence link has changed is not relivant.
40943  *
40944  * Fork - LGPL
40945  * <script type="text/javascript">
40946  */
40947 /**
40948  * @class Roo.form.VTypes
40949  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
40950  * @singleton
40951  */
40952 Roo.form.VTypes = function(){
40953     // closure these in so they are only created once.
40954     var alpha = /^[a-zA-Z_]+$/;
40955     var alphanum = /^[a-zA-Z0-9_]+$/;
40956     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
40957     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
40958
40959     // All these messages and functions are configurable
40960     return {
40961         /**
40962          * The function used to validate email addresses
40963          * @param {String} value The email address
40964          */
40965         'email' : function(v){
40966             return email.test(v);
40967         },
40968         /**
40969          * The error text to display when the email validation function returns false
40970          * @type String
40971          */
40972         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
40973         /**
40974          * The keystroke filter mask to be applied on email input
40975          * @type RegExp
40976          */
40977         'emailMask' : /[a-z0-9_\.\-@]/i,
40978
40979         /**
40980          * The function used to validate URLs
40981          * @param {String} value The URL
40982          */
40983         'url' : function(v){
40984             return url.test(v);
40985         },
40986         /**
40987          * The error text to display when the url validation function returns false
40988          * @type String
40989          */
40990         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
40991         
40992         /**
40993          * The function used to validate alpha values
40994          * @param {String} value The value
40995          */
40996         'alpha' : function(v){
40997             return alpha.test(v);
40998         },
40999         /**
41000          * The error text to display when the alpha validation function returns false
41001          * @type String
41002          */
41003         'alphaText' : 'This field should only contain letters and _',
41004         /**
41005          * The keystroke filter mask to be applied on alpha input
41006          * @type RegExp
41007          */
41008         'alphaMask' : /[a-z_]/i,
41009
41010         /**
41011          * The function used to validate alphanumeric values
41012          * @param {String} value The value
41013          */
41014         'alphanum' : function(v){
41015             return alphanum.test(v);
41016         },
41017         /**
41018          * The error text to display when the alphanumeric validation function returns false
41019          * @type String
41020          */
41021         'alphanumText' : 'This field should only contain letters, numbers and _',
41022         /**
41023          * The keystroke filter mask to be applied on alphanumeric input
41024          * @type RegExp
41025          */
41026         'alphanumMask' : /[a-z0-9_]/i
41027     };
41028 }();//<script type="text/javascript">
41029
41030 /**
41031  * @class Roo.form.FCKeditor
41032  * @extends Roo.form.TextArea
41033  * Wrapper around the FCKEditor http://www.fckeditor.net
41034  * @constructor
41035  * Creates a new FCKeditor
41036  * @param {Object} config Configuration options
41037  */
41038 Roo.form.FCKeditor = function(config){
41039     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41040     this.addEvents({
41041          /**
41042          * @event editorinit
41043          * Fired when the editor is initialized - you can add extra handlers here..
41044          * @param {FCKeditor} this
41045          * @param {Object} the FCK object.
41046          */
41047         editorinit : true
41048     });
41049     
41050     
41051 };
41052 Roo.form.FCKeditor.editors = { };
41053 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41054 {
41055     //defaultAutoCreate : {
41056     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41057     //},
41058     // private
41059     /**
41060      * @cfg {Object} fck options - see fck manual for details.
41061      */
41062     fckconfig : false,
41063     
41064     /**
41065      * @cfg {Object} fck toolbar set (Basic or Default)
41066      */
41067     toolbarSet : 'Basic',
41068     /**
41069      * @cfg {Object} fck BasePath
41070      */ 
41071     basePath : '/fckeditor/',
41072     
41073     
41074     frame : false,
41075     
41076     value : '',
41077     
41078    
41079     onRender : function(ct, position)
41080     {
41081         if(!this.el){
41082             this.defaultAutoCreate = {
41083                 tag: "textarea",
41084                 style:"width:300px;height:60px;",
41085                 autocomplete: "off"
41086             };
41087         }
41088         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41089         /*
41090         if(this.grow){
41091             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41092             if(this.preventScrollbars){
41093                 this.el.setStyle("overflow", "hidden");
41094             }
41095             this.el.setHeight(this.growMin);
41096         }
41097         */
41098         //console.log('onrender' + this.getId() );
41099         Roo.form.FCKeditor.editors[this.getId()] = this;
41100          
41101
41102         this.replaceTextarea() ;
41103         
41104     },
41105     
41106     getEditor : function() {
41107         return this.fckEditor;
41108     },
41109     /**
41110      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41111      * @param {Mixed} value The value to set
41112      */
41113     
41114     
41115     setValue : function(value)
41116     {
41117         //console.log('setValue: ' + value);
41118         
41119         if(typeof(value) == 'undefined') { // not sure why this is happending...
41120             return;
41121         }
41122         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41123         
41124         //if(!this.el || !this.getEditor()) {
41125         //    this.value = value;
41126             //this.setValue.defer(100,this,[value]);    
41127         //    return;
41128         //} 
41129         
41130         if(!this.getEditor()) {
41131             return;
41132         }
41133         
41134         this.getEditor().SetData(value);
41135         
41136         //
41137
41138     },
41139
41140     /**
41141      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41142      * @return {Mixed} value The field value
41143      */
41144     getValue : function()
41145     {
41146         
41147         if (this.frame && this.frame.dom.style.display == 'none') {
41148             return Roo.form.FCKeditor.superclass.getValue.call(this);
41149         }
41150         
41151         if(!this.el || !this.getEditor()) {
41152            
41153            // this.getValue.defer(100,this); 
41154             return this.value;
41155         }
41156        
41157         
41158         var value=this.getEditor().GetData();
41159         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41160         return Roo.form.FCKeditor.superclass.getValue.call(this);
41161         
41162
41163     },
41164
41165     /**
41166      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41167      * @return {Mixed} value The field value
41168      */
41169     getRawValue : function()
41170     {
41171         if (this.frame && this.frame.dom.style.display == 'none') {
41172             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41173         }
41174         
41175         if(!this.el || !this.getEditor()) {
41176             //this.getRawValue.defer(100,this); 
41177             return this.value;
41178             return;
41179         }
41180         
41181         
41182         
41183         var value=this.getEditor().GetData();
41184         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41185         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41186          
41187     },
41188     
41189     setSize : function(w,h) {
41190         
41191         
41192         
41193         //if (this.frame && this.frame.dom.style.display == 'none') {
41194         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41195         //    return;
41196         //}
41197         //if(!this.el || !this.getEditor()) {
41198         //    this.setSize.defer(100,this, [w,h]); 
41199         //    return;
41200         //}
41201         
41202         
41203         
41204         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41205         
41206         this.frame.dom.setAttribute('width', w);
41207         this.frame.dom.setAttribute('height', h);
41208         this.frame.setSize(w,h);
41209         
41210     },
41211     
41212     toggleSourceEdit : function(value) {
41213         
41214       
41215          
41216         this.el.dom.style.display = value ? '' : 'none';
41217         this.frame.dom.style.display = value ?  'none' : '';
41218         
41219     },
41220     
41221     
41222     focus: function(tag)
41223     {
41224         if (this.frame.dom.style.display == 'none') {
41225             return Roo.form.FCKeditor.superclass.focus.call(this);
41226         }
41227         if(!this.el || !this.getEditor()) {
41228             this.focus.defer(100,this, [tag]); 
41229             return;
41230         }
41231         
41232         
41233         
41234         
41235         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41236         this.getEditor().Focus();
41237         if (tgs.length) {
41238             if (!this.getEditor().Selection.GetSelection()) {
41239                 this.focus.defer(100,this, [tag]); 
41240                 return;
41241             }
41242             
41243             
41244             var r = this.getEditor().EditorDocument.createRange();
41245             r.setStart(tgs[0],0);
41246             r.setEnd(tgs[0],0);
41247             this.getEditor().Selection.GetSelection().removeAllRanges();
41248             this.getEditor().Selection.GetSelection().addRange(r);
41249             this.getEditor().Focus();
41250         }
41251         
41252     },
41253     
41254     
41255     
41256     replaceTextarea : function()
41257     {
41258         if ( document.getElementById( this.getId() + '___Frame' ) )
41259             return ;
41260         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41261         //{
41262             // We must check the elements firstly using the Id and then the name.
41263         var oTextarea = document.getElementById( this.getId() );
41264         
41265         var colElementsByName = document.getElementsByName( this.getId() ) ;
41266          
41267         oTextarea.style.display = 'none' ;
41268
41269         if ( oTextarea.tabIndex ) {            
41270             this.TabIndex = oTextarea.tabIndex ;
41271         }
41272         
41273         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41274         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41275         this.frame = Roo.get(this.getId() + '___Frame')
41276     },
41277     
41278     _getConfigHtml : function()
41279     {
41280         var sConfig = '' ;
41281
41282         for ( var o in this.fckconfig ) {
41283             sConfig += sConfig.length > 0  ? '&amp;' : '';
41284             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41285         }
41286
41287         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41288     },
41289     
41290     
41291     _getIFrameHtml : function()
41292     {
41293         var sFile = 'fckeditor.html' ;
41294         /* no idea what this is about..
41295         try
41296         {
41297             if ( (/fcksource=true/i).test( window.top.location.search ) )
41298                 sFile = 'fckeditor.original.html' ;
41299         }
41300         catch (e) { 
41301         */
41302
41303         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41304         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41305         
41306         
41307         var html = '<iframe id="' + this.getId() +
41308             '___Frame" src="' + sLink +
41309             '" width="' + this.width +
41310             '" height="' + this.height + '"' +
41311             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41312             ' frameborder="0" scrolling="no"></iframe>' ;
41313
41314         return html ;
41315     },
41316     
41317     _insertHtmlBefore : function( html, element )
41318     {
41319         if ( element.insertAdjacentHTML )       {
41320             // IE
41321             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41322         } else { // Gecko
41323             var oRange = document.createRange() ;
41324             oRange.setStartBefore( element ) ;
41325             var oFragment = oRange.createContextualFragment( html );
41326             element.parentNode.insertBefore( oFragment, element ) ;
41327         }
41328     }
41329     
41330     
41331   
41332     
41333     
41334     
41335     
41336
41337 });
41338
41339 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41340
41341 function FCKeditor_OnComplete(editorInstance){
41342     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41343     f.fckEditor = editorInstance;
41344     //console.log("loaded");
41345     f.fireEvent('editorinit', f, editorInstance);
41346
41347   
41348
41349  
41350
41351
41352
41353
41354
41355
41356
41357
41358
41359
41360
41361
41362
41363
41364
41365 //<script type="text/javascript">
41366 /**
41367  * @class Roo.form.GridField
41368  * @extends Roo.form.Field
41369  * Embed a grid (or editable grid into a form)
41370  * STATUS ALPHA
41371  * @constructor
41372  * Creates a new GridField
41373  * @param {Object} config Configuration options
41374  */
41375 Roo.form.GridField = function(config){
41376     Roo.form.GridField.superclass.constructor.call(this, config);
41377      
41378 };
41379
41380 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41381     /**
41382      * @cfg {Number} width  - used to restrict width of grid..
41383      */
41384     width : 100,
41385     /**
41386      * @cfg {Number} height - used to restrict height of grid..
41387      */
41388     height : 50,
41389      /**
41390      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41391      */
41392     xgrid : false, 
41393     /**
41394      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41395      * {tag: "input", type: "checkbox", autocomplete: "off"})
41396      */
41397    // defaultAutoCreate : { tag: 'div' },
41398     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41399     /**
41400      * @cfg {String} addTitle Text to include for adding a title.
41401      */
41402     addTitle : false,
41403     //
41404     onResize : function(){
41405         Roo.form.Field.superclass.onResize.apply(this, arguments);
41406     },
41407
41408     initEvents : function(){
41409         // Roo.form.Checkbox.superclass.initEvents.call(this);
41410         // has no events...
41411        
41412     },
41413
41414
41415     getResizeEl : function(){
41416         return this.wrap;
41417     },
41418
41419     getPositionEl : function(){
41420         return this.wrap;
41421     },
41422
41423     // private
41424     onRender : function(ct, position){
41425         
41426         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41427         var style = this.style;
41428         delete this.style;
41429         
41430         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41431         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41432         this.viewEl = this.wrap.createChild({ tag: 'div' });
41433         if (style) {
41434             this.viewEl.applyStyles(style);
41435         }
41436         if (this.width) {
41437             this.viewEl.setWidth(this.width);
41438         }
41439         if (this.height) {
41440             this.viewEl.setHeight(this.height);
41441         }
41442         //if(this.inputValue !== undefined){
41443         //this.setValue(this.value);
41444         
41445         
41446         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41447         
41448         
41449         this.grid.render();
41450         this.grid.getDataSource().on('remove', this.refreshValue, this);
41451         this.grid.getDataSource().on('update', this.refreshValue, this);
41452         this.grid.on('afteredit', this.refreshValue, this);
41453  
41454     },
41455      
41456     
41457     /**
41458      * Sets the value of the item. 
41459      * @param {String} either an object  or a string..
41460      */
41461     setValue : function(v){
41462         //this.value = v;
41463         v = v || []; // empty set..
41464         // this does not seem smart - it really only affects memoryproxy grids..
41465         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41466             var ds = this.grid.getDataSource();
41467             // assumes a json reader..
41468             var data = {}
41469             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41470             ds.loadData( data);
41471         }
41472         Roo.form.GridField.superclass.setValue.call(this, v);
41473         this.refreshValue();
41474         // should load data in the grid really....
41475     },
41476     
41477     // private
41478     refreshValue: function() {
41479          var val = [];
41480         this.grid.getDataSource().each(function(r) {
41481             val.push(r.data);
41482         });
41483         this.el.dom.value = Roo.encode(val);
41484     }
41485     
41486      
41487     
41488     
41489 });//<script type="text/javasscript">
41490  
41491
41492 /**
41493  * @class Roo.DDView
41494  * A DnD enabled version of Roo.View.
41495  * @param {Element/String} container The Element in which to create the View.
41496  * @param {String} tpl The template string used to create the markup for each element of the View
41497  * @param {Object} config The configuration properties. These include all the config options of
41498  * {@link Roo.View} plus some specific to this class.<br>
41499  * <p>
41500  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41501  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41502  * <p>
41503  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41504 .x-view-drag-insert-above {
41505         border-top:1px dotted #3366cc;
41506 }
41507 .x-view-drag-insert-below {
41508         border-bottom:1px dotted #3366cc;
41509 }
41510 </code></pre>
41511  * 
41512  */
41513  
41514 Roo.DDView = function(container, tpl, config) {
41515     Roo.DDView.superclass.constructor.apply(this, arguments);
41516     this.getEl().setStyle("outline", "0px none");
41517     this.getEl().unselectable();
41518     if (this.dragGroup) {
41519                 this.setDraggable(this.dragGroup.split(","));
41520     }
41521     if (this.dropGroup) {
41522                 this.setDroppable(this.dropGroup.split(","));
41523     }
41524     if (this.deletable) {
41525         this.setDeletable();
41526     }
41527     this.isDirtyFlag = false;
41528         this.addEvents({
41529                 "drop" : true
41530         });
41531 };
41532
41533 Roo.extend(Roo.DDView, Roo.View, {
41534 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41535 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41536 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41537 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41538
41539         isFormField: true,
41540
41541         reset: Roo.emptyFn,
41542         
41543         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41544
41545         validate: function() {
41546                 return true;
41547         },
41548         
41549         destroy: function() {
41550                 this.purgeListeners();
41551                 this.getEl.removeAllListeners();
41552                 this.getEl().remove();
41553                 if (this.dragZone) {
41554                         if (this.dragZone.destroy) {
41555                                 this.dragZone.destroy();
41556                         }
41557                 }
41558                 if (this.dropZone) {
41559                         if (this.dropZone.destroy) {
41560                                 this.dropZone.destroy();
41561                         }
41562                 }
41563         },
41564
41565 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41566         getName: function() {
41567                 return this.name;
41568         },
41569
41570 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41571         setValue: function(v) {
41572                 if (!this.store) {
41573                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41574                 }
41575                 var data = {};
41576                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41577                 this.store.proxy = new Roo.data.MemoryProxy(data);
41578                 this.store.load();
41579         },
41580
41581 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41582         getValue: function() {
41583                 var result = '(';
41584                 this.store.each(function(rec) {
41585                         result += rec.id + ',';
41586                 });
41587                 return result.substr(0, result.length - 1) + ')';
41588         },
41589         
41590         getIds: function() {
41591                 var i = 0, result = new Array(this.store.getCount());
41592                 this.store.each(function(rec) {
41593                         result[i++] = rec.id;
41594                 });
41595                 return result;
41596         },
41597         
41598         isDirty: function() {
41599                 return this.isDirtyFlag;
41600         },
41601
41602 /**
41603  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41604  *      whole Element becomes the target, and this causes the drop gesture to append.
41605  */
41606     getTargetFromEvent : function(e) {
41607                 var target = e.getTarget();
41608                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41609                 target = target.parentNode;
41610                 }
41611                 if (!target) {
41612                         target = this.el.dom.lastChild || this.el.dom;
41613                 }
41614                 return target;
41615     },
41616
41617 /**
41618  *      Create the drag data which consists of an object which has the property "ddel" as
41619  *      the drag proxy element. 
41620  */
41621     getDragData : function(e) {
41622         var target = this.findItemFromChild(e.getTarget());
41623                 if(target) {
41624                         this.handleSelection(e);
41625                         var selNodes = this.getSelectedNodes();
41626             var dragData = {
41627                 source: this,
41628                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41629                 nodes: selNodes,
41630                 records: []
41631                         };
41632                         var selectedIndices = this.getSelectedIndexes();
41633                         for (var i = 0; i < selectedIndices.length; i++) {
41634                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41635                         }
41636                         if (selNodes.length == 1) {
41637                                 dragData.ddel = target.cloneNode(true); // the div element
41638                         } else {
41639                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41640                                 div.className = 'multi-proxy';
41641                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41642                                         div.appendChild(selNodes[i].cloneNode(true));
41643                                 }
41644                                 dragData.ddel = div;
41645                         }
41646             //console.log(dragData)
41647             //console.log(dragData.ddel.innerHTML)
41648                         return dragData;
41649                 }
41650         //console.log('nodragData')
41651                 return false;
41652     },
41653     
41654 /**     Specify to which ddGroup items in this DDView may be dragged. */
41655     setDraggable: function(ddGroup) {
41656         if (ddGroup instanceof Array) {
41657                 Roo.each(ddGroup, this.setDraggable, this);
41658                 return;
41659         }
41660         if (this.dragZone) {
41661                 this.dragZone.addToGroup(ddGroup);
41662         } else {
41663                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41664                                 containerScroll: true,
41665                                 ddGroup: ddGroup 
41666
41667                         });
41668 //                      Draggability implies selection. DragZone's mousedown selects the element.
41669                         if (!this.multiSelect) { this.singleSelect = true; }
41670
41671 //                      Wire the DragZone's handlers up to methods in *this*
41672                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41673                 }
41674     },
41675
41676 /**     Specify from which ddGroup this DDView accepts drops. */
41677     setDroppable: function(ddGroup) {
41678         if (ddGroup instanceof Array) {
41679                 Roo.each(ddGroup, this.setDroppable, this);
41680                 return;
41681         }
41682         if (this.dropZone) {
41683                 this.dropZone.addToGroup(ddGroup);
41684         } else {
41685                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41686                                 containerScroll: true,
41687                                 ddGroup: ddGroup
41688                         });
41689
41690 //                      Wire the DropZone's handlers up to methods in *this*
41691                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41692                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41693                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41694                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41695                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41696                 }
41697     },
41698
41699 /**     Decide whether to drop above or below a View node. */
41700     getDropPoint : function(e, n, dd){
41701         if (n == this.el.dom) { return "above"; }
41702                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
41703                 var c = t + (b - t) / 2;
41704                 var y = Roo.lib.Event.getPageY(e);
41705                 if(y <= c) {
41706                         return "above";
41707                 }else{
41708                         return "below";
41709                 }
41710     },
41711
41712     onNodeEnter : function(n, dd, e, data){
41713                 return false;
41714     },
41715     
41716     onNodeOver : function(n, dd, e, data){
41717                 var pt = this.getDropPoint(e, n, dd);
41718                 // set the insert point style on the target node
41719                 var dragElClass = this.dropNotAllowed;
41720                 if (pt) {
41721                         var targetElClass;
41722                         if (pt == "above"){
41723                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
41724                                 targetElClass = "x-view-drag-insert-above";
41725                         } else {
41726                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
41727                                 targetElClass = "x-view-drag-insert-below";
41728                         }
41729                         if (this.lastInsertClass != targetElClass){
41730                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
41731                                 this.lastInsertClass = targetElClass;
41732                         }
41733                 }
41734                 return dragElClass;
41735         },
41736
41737     onNodeOut : function(n, dd, e, data){
41738                 this.removeDropIndicators(n);
41739     },
41740
41741     onNodeDrop : function(n, dd, e, data){
41742         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
41743                 return false;
41744         }
41745         var pt = this.getDropPoint(e, n, dd);
41746                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
41747                 if (pt == "below") { insertAt++; }
41748                 for (var i = 0; i < data.records.length; i++) {
41749                         var r = data.records[i];
41750                         var dup = this.store.getById(r.id);
41751                         if (dup && (dd != this.dragZone)) {
41752                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
41753                         } else {
41754                                 if (data.copy) {
41755                                         this.store.insert(insertAt++, r.copy());
41756                                 } else {
41757                                         data.source.isDirtyFlag = true;
41758                                         r.store.remove(r);
41759                                         this.store.insert(insertAt++, r);
41760                                 }
41761                                 this.isDirtyFlag = true;
41762                         }
41763                 }
41764                 this.dragZone.cachedTarget = null;
41765                 return true;
41766     },
41767
41768     removeDropIndicators : function(n){
41769                 if(n){
41770                         Roo.fly(n).removeClass([
41771                                 "x-view-drag-insert-above",
41772                                 "x-view-drag-insert-below"]);
41773                         this.lastInsertClass = "_noclass";
41774                 }
41775     },
41776
41777 /**
41778  *      Utility method. Add a delete option to the DDView's context menu.
41779  *      @param {String} imageUrl The URL of the "delete" icon image.
41780  */
41781         setDeletable: function(imageUrl) {
41782                 if (!this.singleSelect && !this.multiSelect) {
41783                         this.singleSelect = true;
41784                 }
41785                 var c = this.getContextMenu();
41786                 this.contextMenu.on("itemclick", function(item) {
41787                         switch (item.id) {
41788                                 case "delete":
41789                                         this.remove(this.getSelectedIndexes());
41790                                         break;
41791                         }
41792                 }, this);
41793                 this.contextMenu.add({
41794                         icon: imageUrl,
41795                         id: "delete",
41796                         text: 'Delete'
41797                 });
41798         },
41799         
41800 /**     Return the context menu for this DDView. */
41801         getContextMenu: function() {
41802                 if (!this.contextMenu) {
41803 //                      Create the View's context menu
41804                         this.contextMenu = new Roo.menu.Menu({
41805                                 id: this.id + "-contextmenu"
41806                         });
41807                         this.el.on("contextmenu", this.showContextMenu, this);
41808                 }
41809                 return this.contextMenu;
41810         },
41811         
41812         disableContextMenu: function() {
41813                 if (this.contextMenu) {
41814                         this.el.un("contextmenu", this.showContextMenu, this);
41815                 }
41816         },
41817
41818         showContextMenu: function(e, item) {
41819         item = this.findItemFromChild(e.getTarget());
41820                 if (item) {
41821                         e.stopEvent();
41822                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
41823                         this.contextMenu.showAt(e.getXY());
41824             }
41825     },
41826
41827 /**
41828  *      Remove {@link Roo.data.Record}s at the specified indices.
41829  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
41830  */
41831     remove: function(selectedIndices) {
41832                 selectedIndices = [].concat(selectedIndices);
41833                 for (var i = 0; i < selectedIndices.length; i++) {
41834                         var rec = this.store.getAt(selectedIndices[i]);
41835                         this.store.remove(rec);
41836                 }
41837     },
41838
41839 /**
41840  *      Double click fires the event, but also, if this is draggable, and there is only one other
41841  *      related DropZone, it transfers the selected node.
41842  */
41843     onDblClick : function(e){
41844         var item = this.findItemFromChild(e.getTarget());
41845         if(item){
41846             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
41847                 return false;
41848             }
41849             if (this.dragGroup) {
41850                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
41851                     while (targets.indexOf(this.dropZone) > -1) {
41852                             targets.remove(this.dropZone);
41853                                 }
41854                     if (targets.length == 1) {
41855                                         this.dragZone.cachedTarget = null;
41856                         var el = Roo.get(targets[0].getEl());
41857                         var box = el.getBox(true);
41858                         targets[0].onNodeDrop(el.dom, {
41859                                 target: el.dom,
41860                                 xy: [box.x, box.y + box.height - 1]
41861                         }, null, this.getDragData(e));
41862                     }
41863                 }
41864         }
41865     },
41866     
41867     handleSelection: function(e) {
41868                 this.dragZone.cachedTarget = null;
41869         var item = this.findItemFromChild(e.getTarget());
41870         if (!item) {
41871                 this.clearSelections(true);
41872                 return;
41873         }
41874                 if (item && (this.multiSelect || this.singleSelect)){
41875                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
41876                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
41877                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
41878                                 this.unselect(item);
41879                         } else {
41880                                 this.select(item, this.multiSelect && e.ctrlKey);
41881                                 this.lastSelection = item;
41882                         }
41883                 }
41884     },
41885
41886     onItemClick : function(item, index, e){
41887                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
41888                         return false;
41889                 }
41890                 return true;
41891     },
41892
41893     unselect : function(nodeInfo, suppressEvent){
41894                 var node = this.getNode(nodeInfo);
41895                 if(node && this.isSelected(node)){
41896                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
41897                                 Roo.fly(node).removeClass(this.selectedClass);
41898                                 this.selections.remove(node);
41899                                 if(!suppressEvent){
41900                                         this.fireEvent("selectionchange", this, this.selections);
41901                                 }
41902                         }
41903                 }
41904     }
41905 });
41906 /*
41907  * Based on:
41908  * Ext JS Library 1.1.1
41909  * Copyright(c) 2006-2007, Ext JS, LLC.
41910  *
41911  * Originally Released Under LGPL - original licence link has changed is not relivant.
41912  *
41913  * Fork - LGPL
41914  * <script type="text/javascript">
41915  */
41916  
41917 /**
41918  * @class Roo.LayoutManager
41919  * @extends Roo.util.Observable
41920  * Base class for layout managers.
41921  */
41922 Roo.LayoutManager = function(container, config){
41923     Roo.LayoutManager.superclass.constructor.call(this);
41924     this.el = Roo.get(container);
41925     // ie scrollbar fix
41926     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
41927         document.body.scroll = "no";
41928     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
41929         this.el.position('relative');
41930     }
41931     this.id = this.el.id;
41932     this.el.addClass("x-layout-container");
41933     /** false to disable window resize monitoring @type Boolean */
41934     this.monitorWindowResize = true;
41935     this.regions = {};
41936     this.addEvents({
41937         /**
41938          * @event layout
41939          * Fires when a layout is performed. 
41940          * @param {Roo.LayoutManager} this
41941          */
41942         "layout" : true,
41943         /**
41944          * @event regionresized
41945          * Fires when the user resizes a region. 
41946          * @param {Roo.LayoutRegion} region The resized region
41947          * @param {Number} newSize The new size (width for east/west, height for north/south)
41948          */
41949         "regionresized" : true,
41950         /**
41951          * @event regioncollapsed
41952          * Fires when a region is collapsed. 
41953          * @param {Roo.LayoutRegion} region The collapsed region
41954          */
41955         "regioncollapsed" : true,
41956         /**
41957          * @event regionexpanded
41958          * Fires when a region is expanded.  
41959          * @param {Roo.LayoutRegion} region The expanded region
41960          */
41961         "regionexpanded" : true
41962     });
41963     this.updating = false;
41964     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
41965 };
41966
41967 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
41968     /**
41969      * Returns true if this layout is currently being updated
41970      * @return {Boolean}
41971      */
41972     isUpdating : function(){
41973         return this.updating; 
41974     },
41975     
41976     /**
41977      * Suspend the LayoutManager from doing auto-layouts while
41978      * making multiple add or remove calls
41979      */
41980     beginUpdate : function(){
41981         this.updating = true;    
41982     },
41983     
41984     /**
41985      * Restore auto-layouts and optionally disable the manager from performing a layout
41986      * @param {Boolean} noLayout true to disable a layout update 
41987      */
41988     endUpdate : function(noLayout){
41989         this.updating = false;
41990         if(!noLayout){
41991             this.layout();
41992         }    
41993     },
41994     
41995     layout: function(){
41996         
41997     },
41998     
41999     onRegionResized : function(region, newSize){
42000         this.fireEvent("regionresized", region, newSize);
42001         this.layout();
42002     },
42003     
42004     onRegionCollapsed : function(region){
42005         this.fireEvent("regioncollapsed", region);
42006     },
42007     
42008     onRegionExpanded : function(region){
42009         this.fireEvent("regionexpanded", region);
42010     },
42011         
42012     /**
42013      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42014      * performs box-model adjustments.
42015      * @return {Object} The size as an object {width: (the width), height: (the height)}
42016      */
42017     getViewSize : function(){
42018         var size;
42019         if(this.el.dom != document.body){
42020             size = this.el.getSize();
42021         }else{
42022             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42023         }
42024         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42025         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42026         return size;
42027     },
42028     
42029     /**
42030      * Returns the Element this layout is bound to.
42031      * @return {Roo.Element}
42032      */
42033     getEl : function(){
42034         return this.el;
42035     },
42036     
42037     /**
42038      * Returns the specified region.
42039      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42040      * @return {Roo.LayoutRegion}
42041      */
42042     getRegion : function(target){
42043         return this.regions[target.toLowerCase()];
42044     },
42045     
42046     onWindowResize : function(){
42047         if(this.monitorWindowResize){
42048             this.layout();
42049         }
42050     }
42051 });/*
42052  * Based on:
42053  * Ext JS Library 1.1.1
42054  * Copyright(c) 2006-2007, Ext JS, LLC.
42055  *
42056  * Originally Released Under LGPL - original licence link has changed is not relivant.
42057  *
42058  * Fork - LGPL
42059  * <script type="text/javascript">
42060  */
42061 /**
42062  * @class Roo.BorderLayout
42063  * @extends Roo.LayoutManager
42064  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42065  * please see: <br><br>
42066  * <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>
42067  * <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>
42068  * Example:
42069  <pre><code>
42070  var layout = new Roo.BorderLayout(document.body, {
42071     north: {
42072         initialSize: 25,
42073         titlebar: false
42074     },
42075     west: {
42076         split:true,
42077         initialSize: 200,
42078         minSize: 175,
42079         maxSize: 400,
42080         titlebar: true,
42081         collapsible: true
42082     },
42083     east: {
42084         split:true,
42085         initialSize: 202,
42086         minSize: 175,
42087         maxSize: 400,
42088         titlebar: true,
42089         collapsible: true
42090     },
42091     south: {
42092         split:true,
42093         initialSize: 100,
42094         minSize: 100,
42095         maxSize: 200,
42096         titlebar: true,
42097         collapsible: true
42098     },
42099     center: {
42100         titlebar: true,
42101         autoScroll:true,
42102         resizeTabs: true,
42103         minTabWidth: 50,
42104         preferredTabWidth: 150
42105     }
42106 });
42107
42108 // shorthand
42109 var CP = Roo.ContentPanel;
42110
42111 layout.beginUpdate();
42112 layout.add("north", new CP("north", "North"));
42113 layout.add("south", new CP("south", {title: "South", closable: true}));
42114 layout.add("west", new CP("west", {title: "West"}));
42115 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42116 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42117 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42118 layout.getRegion("center").showPanel("center1");
42119 layout.endUpdate();
42120 </code></pre>
42121
42122 <b>The container the layout is rendered into can be either the body element or any other element.
42123 If it is not the body element, the container needs to either be an absolute positioned element,
42124 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42125 the container size if it is not the body element.</b>
42126
42127 * @constructor
42128 * Create a new BorderLayout
42129 * @param {String/HTMLElement/Element} container The container this layout is bound to
42130 * @param {Object} config Configuration options
42131  */
42132 Roo.BorderLayout = function(container, config){
42133     config = config || {};
42134     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42135     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42136     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42137         var target = this.factory.validRegions[i];
42138         if(config[target]){
42139             this.addRegion(target, config[target]);
42140         }
42141     }
42142 };
42143
42144 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42145     /**
42146      * Creates and adds a new region if it doesn't already exist.
42147      * @param {String} target The target region key (north, south, east, west or center).
42148      * @param {Object} config The regions config object
42149      * @return {BorderLayoutRegion} The new region
42150      */
42151     addRegion : function(target, config){
42152         if(!this.regions[target]){
42153             var r = this.factory.create(target, this, config);
42154             this.bindRegion(target, r);
42155         }
42156         return this.regions[target];
42157     },
42158
42159     // private (kinda)
42160     bindRegion : function(name, r){
42161         this.regions[name] = r;
42162         r.on("visibilitychange", this.layout, this);
42163         r.on("paneladded", this.layout, this);
42164         r.on("panelremoved", this.layout, this);
42165         r.on("invalidated", this.layout, this);
42166         r.on("resized", this.onRegionResized, this);
42167         r.on("collapsed", this.onRegionCollapsed, this);
42168         r.on("expanded", this.onRegionExpanded, this);
42169     },
42170
42171     /**
42172      * Performs a layout update.
42173      */
42174     layout : function(){
42175         if(this.updating) return;
42176         var size = this.getViewSize();
42177         var w = size.width;
42178         var h = size.height;
42179         var centerW = w;
42180         var centerH = h;
42181         var centerY = 0;
42182         var centerX = 0;
42183         //var x = 0, y = 0;
42184
42185         var rs = this.regions;
42186         var north = rs["north"];
42187         var south = rs["south"]; 
42188         var west = rs["west"];
42189         var east = rs["east"];
42190         var center = rs["center"];
42191         //if(this.hideOnLayout){ // not supported anymore
42192             //c.el.setStyle("display", "none");
42193         //}
42194         if(north && north.isVisible()){
42195             var b = north.getBox();
42196             var m = north.getMargins();
42197             b.width = w - (m.left+m.right);
42198             b.x = m.left;
42199             b.y = m.top;
42200             centerY = b.height + b.y + m.bottom;
42201             centerH -= centerY;
42202             north.updateBox(this.safeBox(b));
42203         }
42204         if(south && south.isVisible()){
42205             var b = south.getBox();
42206             var m = south.getMargins();
42207             b.width = w - (m.left+m.right);
42208             b.x = m.left;
42209             var totalHeight = (b.height + m.top + m.bottom);
42210             b.y = h - totalHeight + m.top;
42211             centerH -= totalHeight;
42212             south.updateBox(this.safeBox(b));
42213         }
42214         if(west && west.isVisible()){
42215             var b = west.getBox();
42216             var m = west.getMargins();
42217             b.height = centerH - (m.top+m.bottom);
42218             b.x = m.left;
42219             b.y = centerY + m.top;
42220             var totalWidth = (b.width + m.left + m.right);
42221             centerX += totalWidth;
42222             centerW -= totalWidth;
42223             west.updateBox(this.safeBox(b));
42224         }
42225         if(east && east.isVisible()){
42226             var b = east.getBox();
42227             var m = east.getMargins();
42228             b.height = centerH - (m.top+m.bottom);
42229             var totalWidth = (b.width + m.left + m.right);
42230             b.x = w - totalWidth + m.left;
42231             b.y = centerY + m.top;
42232             centerW -= totalWidth;
42233             east.updateBox(this.safeBox(b));
42234         }
42235         if(center){
42236             var m = center.getMargins();
42237             var centerBox = {
42238                 x: centerX + m.left,
42239                 y: centerY + m.top,
42240                 width: centerW - (m.left+m.right),
42241                 height: centerH - (m.top+m.bottom)
42242             };
42243             //if(this.hideOnLayout){
42244                 //center.el.setStyle("display", "block");
42245             //}
42246             center.updateBox(this.safeBox(centerBox));
42247         }
42248         this.el.repaint();
42249         this.fireEvent("layout", this);
42250     },
42251
42252     // private
42253     safeBox : function(box){
42254         box.width = Math.max(0, box.width);
42255         box.height = Math.max(0, box.height);
42256         return box;
42257     },
42258
42259     /**
42260      * Adds a ContentPanel (or subclass) to this layout.
42261      * @param {String} target The target region key (north, south, east, west or center).
42262      * @param {Roo.ContentPanel} panel The panel to add
42263      * @return {Roo.ContentPanel} The added panel
42264      */
42265     add : function(target, panel){
42266          
42267         target = target.toLowerCase();
42268         return this.regions[target].add(panel);
42269     },
42270
42271     /**
42272      * Remove a ContentPanel (or subclass) to this layout.
42273      * @param {String} target The target region key (north, south, east, west or center).
42274      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42275      * @return {Roo.ContentPanel} The removed panel
42276      */
42277     remove : function(target, panel){
42278         target = target.toLowerCase();
42279         return this.regions[target].remove(panel);
42280     },
42281
42282     /**
42283      * Searches all regions for a panel with the specified id
42284      * @param {String} panelId
42285      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42286      */
42287     findPanel : function(panelId){
42288         var rs = this.regions;
42289         for(var target in rs){
42290             if(typeof rs[target] != "function"){
42291                 var p = rs[target].getPanel(panelId);
42292                 if(p){
42293                     return p;
42294                 }
42295             }
42296         }
42297         return null;
42298     },
42299
42300     /**
42301      * Searches all regions for a panel with the specified id and activates (shows) it.
42302      * @param {String/ContentPanel} panelId The panels id or the panel itself
42303      * @return {Roo.ContentPanel} The shown panel or null
42304      */
42305     showPanel : function(panelId) {
42306       var rs = this.regions;
42307       for(var target in rs){
42308          var r = rs[target];
42309          if(typeof r != "function"){
42310             if(r.hasPanel(panelId)){
42311                return r.showPanel(panelId);
42312             }
42313          }
42314       }
42315       return null;
42316    },
42317
42318    /**
42319      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42320      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42321      */
42322     restoreState : function(provider){
42323         if(!provider){
42324             provider = Roo.state.Manager;
42325         }
42326         var sm = new Roo.LayoutStateManager();
42327         sm.init(this, provider);
42328     },
42329
42330     /**
42331      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42332      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42333      * a valid ContentPanel config object.  Example:
42334      * <pre><code>
42335 // Create the main layout
42336 var layout = new Roo.BorderLayout('main-ct', {
42337     west: {
42338         split:true,
42339         minSize: 175,
42340         titlebar: true
42341     },
42342     center: {
42343         title:'Components'
42344     }
42345 }, 'main-ct');
42346
42347 // Create and add multiple ContentPanels at once via configs
42348 layout.batchAdd({
42349    west: {
42350        id: 'source-files',
42351        autoCreate:true,
42352        title:'Ext Source Files',
42353        autoScroll:true,
42354        fitToFrame:true
42355    },
42356    center : {
42357        el: cview,
42358        autoScroll:true,
42359        fitToFrame:true,
42360        toolbar: tb,
42361        resizeEl:'cbody'
42362    }
42363 });
42364 </code></pre>
42365      * @param {Object} regions An object containing ContentPanel configs by region name
42366      */
42367     batchAdd : function(regions){
42368         this.beginUpdate();
42369         for(var rname in regions){
42370             var lr = this.regions[rname];
42371             if(lr){
42372                 this.addTypedPanels(lr, regions[rname]);
42373             }
42374         }
42375         this.endUpdate();
42376     },
42377
42378     // private
42379     addTypedPanels : function(lr, ps){
42380         if(typeof ps == 'string'){
42381             lr.add(new Roo.ContentPanel(ps));
42382         }
42383         else if(ps instanceof Array){
42384             for(var i =0, len = ps.length; i < len; i++){
42385                 this.addTypedPanels(lr, ps[i]);
42386             }
42387         }
42388         else if(!ps.events){ // raw config?
42389             var el = ps.el;
42390             delete ps.el; // prevent conflict
42391             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42392         }
42393         else {  // panel object assumed!
42394             lr.add(ps);
42395         }
42396     },
42397     /**
42398      * Adds a xtype elements to the layout.
42399      * <pre><code>
42400
42401 layout.addxtype({
42402        xtype : 'ContentPanel',
42403        region: 'west',
42404        items: [ .... ]
42405    }
42406 );
42407
42408 layout.addxtype({
42409         xtype : 'NestedLayoutPanel',
42410         region: 'west',
42411         layout: {
42412            center: { },
42413            west: { }   
42414         },
42415         items : [ ... list of content panels or nested layout panels.. ]
42416    }
42417 );
42418 </code></pre>
42419      * @param {Object} cfg Xtype definition of item to add.
42420      */
42421     addxtype : function(cfg)
42422     {
42423         // basically accepts a pannel...
42424         // can accept a layout region..!?!?
42425        // console.log('BorderLayout add ' + cfg.xtype)
42426         
42427         if (!cfg.xtype.match(/Panel$/)) {
42428             return false;
42429         }
42430         var ret = false;
42431         var region = cfg.region;
42432         delete cfg.region;
42433         
42434           
42435         var xitems = [];
42436         if (cfg.items) {
42437             xitems = cfg.items;
42438             delete cfg.items;
42439         }
42440         
42441         
42442         switch(cfg.xtype) 
42443         {
42444             case 'ContentPanel':  // ContentPanel (el, cfg)
42445                 if(cfg.autoCreate) {
42446                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42447                 } else {
42448                     var el = this.el.createChild();
42449                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42450                 }
42451                 
42452                 this.add(region, ret);
42453                 break;
42454             
42455             
42456             case 'TreePanel': // our new panel!
42457                 cfg.el = this.el.createChild();
42458                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42459                 this.add(region, ret);
42460                 break;
42461             
42462             case 'NestedLayoutPanel': 
42463                 // create a new Layout (which is  a Border Layout...
42464                 var el = this.el.createChild();
42465                 var clayout = cfg.layout;
42466                 delete cfg.layout;
42467                 clayout.items   = clayout.items  || [];
42468                 // replace this exitems with the clayout ones..
42469                 xitems = clayout.items;
42470                  
42471                 
42472                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42473                     cfg.background = false;
42474                 }
42475                 var layout = new Roo.BorderLayout(el, clayout);
42476                 
42477                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42478                 //console.log('adding nested layout panel '  + cfg.toSource());
42479                 this.add(region, ret);
42480                 
42481                 break;
42482                 
42483             case 'GridPanel': 
42484             
42485                 // needs grid and region
42486                 
42487                 //var el = this.getRegion(region).el.createChild();
42488                 var el = this.el.createChild();
42489                 // create the grid first...
42490                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42491                 grid.xmodule = this.xmodule || false;
42492                 delete cfg.grid;
42493                 if (region == 'center' && this.active ) {
42494                     cfg.background = false;
42495                 }
42496                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42497                 
42498                 this.add(region, ret);
42499                 if (cfg.background) {
42500                     ret.on('activate', function(gp) {
42501                         if (!gp.grid.rendered) {
42502                             gp.grid.render();
42503                         }
42504                     });
42505                 } else {
42506                     grid.render();
42507                 }
42508                 break;
42509            
42510                
42511                 
42512                 
42513             default: 
42514                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42515                 return;
42516              // GridPanel (grid, cfg)
42517             
42518         }
42519         this.beginUpdate();
42520         // add children..
42521         Roo.each(xitems, function(i)  {
42522             ret.addxtype(i);
42523         });
42524         this.endUpdate();
42525         return ret;
42526         
42527     }
42528 });
42529
42530 /**
42531  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42532  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42533  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42534  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42535  * <pre><code>
42536 // shorthand
42537 var CP = Roo.ContentPanel;
42538
42539 var layout = Roo.BorderLayout.create({
42540     north: {
42541         initialSize: 25,
42542         titlebar: false,
42543         panels: [new CP("north", "North")]
42544     },
42545     west: {
42546         split:true,
42547         initialSize: 200,
42548         minSize: 175,
42549         maxSize: 400,
42550         titlebar: true,
42551         collapsible: true,
42552         panels: [new CP("west", {title: "West"})]
42553     },
42554     east: {
42555         split:true,
42556         initialSize: 202,
42557         minSize: 175,
42558         maxSize: 400,
42559         titlebar: true,
42560         collapsible: true,
42561         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42562     },
42563     south: {
42564         split:true,
42565         initialSize: 100,
42566         minSize: 100,
42567         maxSize: 200,
42568         titlebar: true,
42569         collapsible: true,
42570         panels: [new CP("south", {title: "South", closable: true})]
42571     },
42572     center: {
42573         titlebar: true,
42574         autoScroll:true,
42575         resizeTabs: true,
42576         minTabWidth: 50,
42577         preferredTabWidth: 150,
42578         panels: [
42579             new CP("center1", {title: "Close Me", closable: true}),
42580             new CP("center2", {title: "Center Panel", closable: false})
42581         ]
42582     }
42583 }, document.body);
42584
42585 layout.getRegion("center").showPanel("center1");
42586 </code></pre>
42587  * @param config
42588  * @param targetEl
42589  */
42590 Roo.BorderLayout.create = function(config, targetEl){
42591     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42592     layout.beginUpdate();
42593     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42594     for(var j = 0, jlen = regions.length; j < jlen; j++){
42595         var lr = regions[j];
42596         if(layout.regions[lr] && config[lr].panels){
42597             var r = layout.regions[lr];
42598             var ps = config[lr].panels;
42599             layout.addTypedPanels(r, ps);
42600         }
42601     }
42602     layout.endUpdate();
42603     return layout;
42604 };
42605
42606 // private
42607 Roo.BorderLayout.RegionFactory = {
42608     // private
42609     validRegions : ["north","south","east","west","center"],
42610
42611     // private
42612     create : function(target, mgr, config){
42613         target = target.toLowerCase();
42614         if(config.lightweight || config.basic){
42615             return new Roo.BasicLayoutRegion(mgr, config, target);
42616         }
42617         switch(target){
42618             case "north":
42619                 return new Roo.NorthLayoutRegion(mgr, config);
42620             case "south":
42621                 return new Roo.SouthLayoutRegion(mgr, config);
42622             case "east":
42623                 return new Roo.EastLayoutRegion(mgr, config);
42624             case "west":
42625                 return new Roo.WestLayoutRegion(mgr, config);
42626             case "center":
42627                 return new Roo.CenterLayoutRegion(mgr, config);
42628         }
42629         throw 'Layout region "'+target+'" not supported.';
42630     }
42631 };/*
42632  * Based on:
42633  * Ext JS Library 1.1.1
42634  * Copyright(c) 2006-2007, Ext JS, LLC.
42635  *
42636  * Originally Released Under LGPL - original licence link has changed is not relivant.
42637  *
42638  * Fork - LGPL
42639  * <script type="text/javascript">
42640  */
42641  
42642 /**
42643  * @class Roo.BasicLayoutRegion
42644  * @extends Roo.util.Observable
42645  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42646  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42647  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42648  */
42649 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42650     this.mgr = mgr;
42651     this.position  = pos;
42652     this.events = {
42653         /**
42654          * @scope Roo.BasicLayoutRegion
42655          */
42656         
42657         /**
42658          * @event beforeremove
42659          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42660          * @param {Roo.LayoutRegion} this
42661          * @param {Roo.ContentPanel} panel The panel
42662          * @param {Object} e The cancel event object
42663          */
42664         "beforeremove" : true,
42665         /**
42666          * @event invalidated
42667          * Fires when the layout for this region is changed.
42668          * @param {Roo.LayoutRegion} this
42669          */
42670         "invalidated" : true,
42671         /**
42672          * @event visibilitychange
42673          * Fires when this region is shown or hidden 
42674          * @param {Roo.LayoutRegion} this
42675          * @param {Boolean} visibility true or false
42676          */
42677         "visibilitychange" : true,
42678         /**
42679          * @event paneladded
42680          * Fires when a panel is added. 
42681          * @param {Roo.LayoutRegion} this
42682          * @param {Roo.ContentPanel} panel The panel
42683          */
42684         "paneladded" : true,
42685         /**
42686          * @event panelremoved
42687          * Fires when a panel is removed. 
42688          * @param {Roo.LayoutRegion} this
42689          * @param {Roo.ContentPanel} panel The panel
42690          */
42691         "panelremoved" : true,
42692         /**
42693          * @event collapsed
42694          * Fires when this region is collapsed.
42695          * @param {Roo.LayoutRegion} this
42696          */
42697         "collapsed" : true,
42698         /**
42699          * @event expanded
42700          * Fires when this region is expanded.
42701          * @param {Roo.LayoutRegion} this
42702          */
42703         "expanded" : true,
42704         /**
42705          * @event slideshow
42706          * Fires when this region is slid into view.
42707          * @param {Roo.LayoutRegion} this
42708          */
42709         "slideshow" : true,
42710         /**
42711          * @event slidehide
42712          * Fires when this region slides out of view. 
42713          * @param {Roo.LayoutRegion} this
42714          */
42715         "slidehide" : true,
42716         /**
42717          * @event panelactivated
42718          * Fires when a panel is activated. 
42719          * @param {Roo.LayoutRegion} this
42720          * @param {Roo.ContentPanel} panel The activated panel
42721          */
42722         "panelactivated" : true,
42723         /**
42724          * @event resized
42725          * Fires when the user resizes this region. 
42726          * @param {Roo.LayoutRegion} this
42727          * @param {Number} newSize The new size (width for east/west, height for north/south)
42728          */
42729         "resized" : true
42730     };
42731     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42732     this.panels = new Roo.util.MixedCollection();
42733     this.panels.getKey = this.getPanelId.createDelegate(this);
42734     this.box = null;
42735     this.activePanel = null;
42736     // ensure listeners are added...
42737     
42738     if (config.listeners || config.events) {
42739         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
42740             listeners : config.listeners || {},
42741             events : config.events || {}
42742         });
42743     }
42744     
42745     if(skipConfig !== true){
42746         this.applyConfig(config);
42747     }
42748 };
42749
42750 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
42751     getPanelId : function(p){
42752         return p.getId();
42753     },
42754     
42755     applyConfig : function(config){
42756         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42757         this.config = config;
42758         
42759     },
42760     
42761     /**
42762      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42763      * the width, for horizontal (north, south) the height.
42764      * @param {Number} newSize The new width or height
42765      */
42766     resizeTo : function(newSize){
42767         var el = this.el ? this.el :
42768                  (this.activePanel ? this.activePanel.getEl() : null);
42769         if(el){
42770             switch(this.position){
42771                 case "east":
42772                 case "west":
42773                     el.setWidth(newSize);
42774                     this.fireEvent("resized", this, newSize);
42775                 break;
42776                 case "north":
42777                 case "south":
42778                     el.setHeight(newSize);
42779                     this.fireEvent("resized", this, newSize);
42780                 break;                
42781             }
42782         }
42783     },
42784     
42785     getBox : function(){
42786         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42787     },
42788     
42789     getMargins : function(){
42790         return this.margins;
42791     },
42792     
42793     updateBox : function(box){
42794         this.box = box;
42795         var el = this.activePanel.getEl();
42796         el.dom.style.left = box.x + "px";
42797         el.dom.style.top = box.y + "px";
42798         this.activePanel.setSize(box.width, box.height);
42799     },
42800     
42801     /**
42802      * Returns the container element for this region.
42803      * @return {Roo.Element}
42804      */
42805     getEl : function(){
42806         return this.activePanel;
42807     },
42808     
42809     /**
42810      * Returns true if this region is currently visible.
42811      * @return {Boolean}
42812      */
42813     isVisible : function(){
42814         return this.activePanel ? true : false;
42815     },
42816     
42817     setActivePanel : function(panel){
42818         panel = this.getPanel(panel);
42819         if(this.activePanel && this.activePanel != panel){
42820             this.activePanel.setActiveState(false);
42821             this.activePanel.getEl().setLeftTop(-10000,-10000);
42822         }
42823         this.activePanel = panel;
42824         panel.setActiveState(true);
42825         if(this.box){
42826             panel.setSize(this.box.width, this.box.height);
42827         }
42828         this.fireEvent("panelactivated", this, panel);
42829         this.fireEvent("invalidated");
42830     },
42831     
42832     /**
42833      * Show the specified panel.
42834      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42835      * @return {Roo.ContentPanel} The shown panel or null
42836      */
42837     showPanel : function(panel){
42838         if(panel = this.getPanel(panel)){
42839             this.setActivePanel(panel);
42840         }
42841         return panel;
42842     },
42843     
42844     /**
42845      * Get the active panel for this region.
42846      * @return {Roo.ContentPanel} The active panel or null
42847      */
42848     getActivePanel : function(){
42849         return this.activePanel;
42850     },
42851     
42852     /**
42853      * Add the passed ContentPanel(s)
42854      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42855      * @return {Roo.ContentPanel} The panel added (if only one was added)
42856      */
42857     add : function(panel){
42858         if(arguments.length > 1){
42859             for(var i = 0, len = arguments.length; i < len; i++) {
42860                 this.add(arguments[i]);
42861             }
42862             return null;
42863         }
42864         if(this.hasPanel(panel)){
42865             this.showPanel(panel);
42866             return panel;
42867         }
42868         var el = panel.getEl();
42869         if(el.dom.parentNode != this.mgr.el.dom){
42870             this.mgr.el.dom.appendChild(el.dom);
42871         }
42872         if(panel.setRegion){
42873             panel.setRegion(this);
42874         }
42875         this.panels.add(panel);
42876         el.setStyle("position", "absolute");
42877         if(!panel.background){
42878             this.setActivePanel(panel);
42879             if(this.config.initialSize && this.panels.getCount()==1){
42880                 this.resizeTo(this.config.initialSize);
42881             }
42882         }
42883         this.fireEvent("paneladded", this, panel);
42884         return panel;
42885     },
42886     
42887     /**
42888      * Returns true if the panel is in this region.
42889      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42890      * @return {Boolean}
42891      */
42892     hasPanel : function(panel){
42893         if(typeof panel == "object"){ // must be panel obj
42894             panel = panel.getId();
42895         }
42896         return this.getPanel(panel) ? true : false;
42897     },
42898     
42899     /**
42900      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42901      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42902      * @param {Boolean} preservePanel Overrides the config preservePanel option
42903      * @return {Roo.ContentPanel} The panel that was removed
42904      */
42905     remove : function(panel, preservePanel){
42906         panel = this.getPanel(panel);
42907         if(!panel){
42908             return null;
42909         }
42910         var e = {};
42911         this.fireEvent("beforeremove", this, panel, e);
42912         if(e.cancel === true){
42913             return null;
42914         }
42915         var panelId = panel.getId();
42916         this.panels.removeKey(panelId);
42917         return panel;
42918     },
42919     
42920     /**
42921      * Returns the panel specified or null if it's not in this region.
42922      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42923      * @return {Roo.ContentPanel}
42924      */
42925     getPanel : function(id){
42926         if(typeof id == "object"){ // must be panel obj
42927             return id;
42928         }
42929         return this.panels.get(id);
42930     },
42931     
42932     /**
42933      * Returns this regions position (north/south/east/west/center).
42934      * @return {String} 
42935      */
42936     getPosition: function(){
42937         return this.position;    
42938     }
42939 });/*
42940  * Based on:
42941  * Ext JS Library 1.1.1
42942  * Copyright(c) 2006-2007, Ext JS, LLC.
42943  *
42944  * Originally Released Under LGPL - original licence link has changed is not relivant.
42945  *
42946  * Fork - LGPL
42947  * <script type="text/javascript">
42948  */
42949  
42950 /**
42951  * @class Roo.LayoutRegion
42952  * @extends Roo.BasicLayoutRegion
42953  * This class represents a region in a layout manager.
42954  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
42955  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
42956  * @cfg {Boolean} floatable False to disable floating (defaults to true)
42957  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
42958  * @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})
42959  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
42960  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
42961  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
42962  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
42963  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
42964  * @cfg {String} title The title for the region (overrides panel titles)
42965  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
42966  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
42967  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
42968  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
42969  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
42970  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
42971  * the space available, similar to FireFox 1.5 tabs (defaults to false)
42972  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
42973  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
42974  * @cfg {Boolean} showPin True to show a pin button
42975 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
42976 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
42977 * @cfg {Boolean} disableTabTips True to disable tab tooltips
42978 * @cfg {Number} width  For East/West panels
42979 * @cfg {Number} height For North/South panels
42980 * @cfg {Boolean} split To show the splitter
42981  */
42982 Roo.LayoutRegion = function(mgr, config, pos){
42983     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
42984     var dh = Roo.DomHelper;
42985     /** This region's container element 
42986     * @type Roo.Element */
42987     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
42988     /** This region's title element 
42989     * @type Roo.Element */
42990
42991     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
42992         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
42993         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
42994     ]}, true);
42995     this.titleEl.enableDisplayMode();
42996     /** This region's title text element 
42997     * @type HTMLElement */
42998     this.titleTextEl = this.titleEl.dom.firstChild;
42999     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43000     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43001     this.closeBtn.enableDisplayMode();
43002     this.closeBtn.on("click", this.closeClicked, this);
43003     this.closeBtn.hide();
43004
43005     this.createBody(config);
43006     this.visible = true;
43007     this.collapsed = false;
43008
43009     if(config.hideWhenEmpty){
43010         this.hide();
43011         this.on("paneladded", this.validateVisibility, this);
43012         this.on("panelremoved", this.validateVisibility, this);
43013     }
43014     this.applyConfig(config);
43015 };
43016
43017 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43018
43019     createBody : function(){
43020         /** This region's body element 
43021         * @type Roo.Element */
43022         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43023     },
43024
43025     applyConfig : function(c){
43026         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43027             var dh = Roo.DomHelper;
43028             if(c.titlebar !== false){
43029                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43030                 this.collapseBtn.on("click", this.collapse, this);
43031                 this.collapseBtn.enableDisplayMode();
43032
43033                 if(c.showPin === true || this.showPin){
43034                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43035                     this.stickBtn.enableDisplayMode();
43036                     this.stickBtn.on("click", this.expand, this);
43037                     this.stickBtn.hide();
43038                 }
43039             }
43040             /** This region's collapsed element
43041             * @type Roo.Element */
43042             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43043                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43044             ]}, true);
43045             if(c.floatable !== false){
43046                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43047                this.collapsedEl.on("click", this.collapseClick, this);
43048             }
43049
43050             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43051                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43052                    id: "message", unselectable: "on", style:{"float":"left"}});
43053                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43054              }
43055             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43056             this.expandBtn.on("click", this.expand, this);
43057         }
43058         if(this.collapseBtn){
43059             this.collapseBtn.setVisible(c.collapsible == true);
43060         }
43061         this.cmargins = c.cmargins || this.cmargins ||
43062                          (this.position == "west" || this.position == "east" ?
43063                              {top: 0, left: 2, right:2, bottom: 0} :
43064                              {top: 2, left: 0, right:0, bottom: 2});
43065         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43066         this.bottomTabs = c.tabPosition != "top";
43067         this.autoScroll = c.autoScroll || false;
43068         if(this.autoScroll){
43069             this.bodyEl.setStyle("overflow", "auto");
43070         }else{
43071             this.bodyEl.setStyle("overflow", "hidden");
43072         }
43073         //if(c.titlebar !== false){
43074             if((!c.titlebar && !c.title) || c.titlebar === false){
43075                 this.titleEl.hide();
43076             }else{
43077                 this.titleEl.show();
43078                 if(c.title){
43079                     this.titleTextEl.innerHTML = c.title;
43080                 }
43081             }
43082         //}
43083         this.duration = c.duration || .30;
43084         this.slideDuration = c.slideDuration || .45;
43085         this.config = c;
43086         if(c.collapsed){
43087             this.collapse(true);
43088         }
43089         if(c.hidden){
43090             this.hide();
43091         }
43092     },
43093     /**
43094      * Returns true if this region is currently visible.
43095      * @return {Boolean}
43096      */
43097     isVisible : function(){
43098         return this.visible;
43099     },
43100
43101     /**
43102      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43103      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43104      */
43105     setCollapsedTitle : function(title){
43106         title = title || "&#160;";
43107         if(this.collapsedTitleTextEl){
43108             this.collapsedTitleTextEl.innerHTML = title;
43109         }
43110     },
43111
43112     getBox : function(){
43113         var b;
43114         if(!this.collapsed){
43115             b = this.el.getBox(false, true);
43116         }else{
43117             b = this.collapsedEl.getBox(false, true);
43118         }
43119         return b;
43120     },
43121
43122     getMargins : function(){
43123         return this.collapsed ? this.cmargins : this.margins;
43124     },
43125
43126     highlight : function(){
43127         this.el.addClass("x-layout-panel-dragover");
43128     },
43129
43130     unhighlight : function(){
43131         this.el.removeClass("x-layout-panel-dragover");
43132     },
43133
43134     updateBox : function(box){
43135         this.box = box;
43136         if(!this.collapsed){
43137             this.el.dom.style.left = box.x + "px";
43138             this.el.dom.style.top = box.y + "px";
43139             this.updateBody(box.width, box.height);
43140         }else{
43141             this.collapsedEl.dom.style.left = box.x + "px";
43142             this.collapsedEl.dom.style.top = box.y + "px";
43143             this.collapsedEl.setSize(box.width, box.height);
43144         }
43145         if(this.tabs){
43146             this.tabs.autoSizeTabs();
43147         }
43148     },
43149
43150     updateBody : function(w, h){
43151         if(w !== null){
43152             this.el.setWidth(w);
43153             w -= this.el.getBorderWidth("rl");
43154             if(this.config.adjustments){
43155                 w += this.config.adjustments[0];
43156             }
43157         }
43158         if(h !== null){
43159             this.el.setHeight(h);
43160             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43161             h -= this.el.getBorderWidth("tb");
43162             if(this.config.adjustments){
43163                 h += this.config.adjustments[1];
43164             }
43165             this.bodyEl.setHeight(h);
43166             if(this.tabs){
43167                 h = this.tabs.syncHeight(h);
43168             }
43169         }
43170         if(this.panelSize){
43171             w = w !== null ? w : this.panelSize.width;
43172             h = h !== null ? h : this.panelSize.height;
43173         }
43174         if(this.activePanel){
43175             var el = this.activePanel.getEl();
43176             w = w !== null ? w : el.getWidth();
43177             h = h !== null ? h : el.getHeight();
43178             this.panelSize = {width: w, height: h};
43179             this.activePanel.setSize(w, h);
43180         }
43181         if(Roo.isIE && this.tabs){
43182             this.tabs.el.repaint();
43183         }
43184     },
43185
43186     /**
43187      * Returns the container element for this region.
43188      * @return {Roo.Element}
43189      */
43190     getEl : function(){
43191         return this.el;
43192     },
43193
43194     /**
43195      * Hides this region.
43196      */
43197     hide : function(){
43198         if(!this.collapsed){
43199             this.el.dom.style.left = "-2000px";
43200             this.el.hide();
43201         }else{
43202             this.collapsedEl.dom.style.left = "-2000px";
43203             this.collapsedEl.hide();
43204         }
43205         this.visible = false;
43206         this.fireEvent("visibilitychange", this, false);
43207     },
43208
43209     /**
43210      * Shows this region if it was previously hidden.
43211      */
43212     show : function(){
43213         if(!this.collapsed){
43214             this.el.show();
43215         }else{
43216             this.collapsedEl.show();
43217         }
43218         this.visible = true;
43219         this.fireEvent("visibilitychange", this, true);
43220     },
43221
43222     closeClicked : function(){
43223         if(this.activePanel){
43224             this.remove(this.activePanel);
43225         }
43226     },
43227
43228     collapseClick : function(e){
43229         if(this.isSlid){
43230            e.stopPropagation();
43231            this.slideIn();
43232         }else{
43233            e.stopPropagation();
43234            this.slideOut();
43235         }
43236     },
43237
43238     /**
43239      * Collapses this region.
43240      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43241      */
43242     collapse : function(skipAnim){
43243         if(this.collapsed) return;
43244         this.collapsed = true;
43245         if(this.split){
43246             this.split.el.hide();
43247         }
43248         if(this.config.animate && skipAnim !== true){
43249             this.fireEvent("invalidated", this);
43250             this.animateCollapse();
43251         }else{
43252             this.el.setLocation(-20000,-20000);
43253             this.el.hide();
43254             this.collapsedEl.show();
43255             this.fireEvent("collapsed", this);
43256             this.fireEvent("invalidated", this);
43257         }
43258     },
43259
43260     animateCollapse : function(){
43261         // overridden
43262     },
43263
43264     /**
43265      * Expands this region if it was previously collapsed.
43266      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43267      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43268      */
43269     expand : function(e, skipAnim){
43270         if(e) e.stopPropagation();
43271         if(!this.collapsed || this.el.hasActiveFx()) return;
43272         if(this.isSlid){
43273             this.afterSlideIn();
43274             skipAnim = true;
43275         }
43276         this.collapsed = false;
43277         if(this.config.animate && skipAnim !== true){
43278             this.animateExpand();
43279         }else{
43280             this.el.show();
43281             if(this.split){
43282                 this.split.el.show();
43283             }
43284             this.collapsedEl.setLocation(-2000,-2000);
43285             this.collapsedEl.hide();
43286             this.fireEvent("invalidated", this);
43287             this.fireEvent("expanded", this);
43288         }
43289     },
43290
43291     animateExpand : function(){
43292         // overridden
43293     },
43294
43295     initTabs : function(){
43296         this.bodyEl.setStyle("overflow", "hidden");
43297         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43298             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43299             disableTooltips: this.config.disableTabTips
43300         });
43301         if(this.config.hideTabs){
43302             ts.stripWrap.setDisplayed(false);
43303         }
43304         this.tabs = ts;
43305         ts.resizeTabs = this.config.resizeTabs === true;
43306         ts.minTabWidth = this.config.minTabWidth || 40;
43307         ts.maxTabWidth = this.config.maxTabWidth || 250;
43308         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43309         ts.monitorResize = false;
43310         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43311         ts.bodyEl.addClass('x-layout-tabs-body');
43312         this.panels.each(this.initPanelAsTab, this);
43313     },
43314
43315     initPanelAsTab : function(panel){
43316         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43317                     this.config.closeOnTab && panel.isClosable());
43318         if(panel.tabTip !== undefined){
43319             ti.setTooltip(panel.tabTip);
43320         }
43321         ti.on("activate", function(){
43322               this.setActivePanel(panel);
43323         }, this);
43324         if(this.config.closeOnTab){
43325             ti.on("beforeclose", function(t, e){
43326                 e.cancel = true;
43327                 this.remove(panel);
43328             }, this);
43329         }
43330         return ti;
43331     },
43332
43333     updatePanelTitle : function(panel, title){
43334         if(this.activePanel == panel){
43335             this.updateTitle(title);
43336         }
43337         if(this.tabs){
43338             var ti = this.tabs.getTab(panel.getEl().id);
43339             ti.setText(title);
43340             if(panel.tabTip !== undefined){
43341                 ti.setTooltip(panel.tabTip);
43342             }
43343         }
43344     },
43345
43346     updateTitle : function(title){
43347         if(this.titleTextEl && !this.config.title){
43348             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43349         }
43350     },
43351
43352     setActivePanel : function(panel){
43353         panel = this.getPanel(panel);
43354         if(this.activePanel && this.activePanel != panel){
43355             this.activePanel.setActiveState(false);
43356         }
43357         this.activePanel = panel;
43358         panel.setActiveState(true);
43359         if(this.panelSize){
43360             panel.setSize(this.panelSize.width, this.panelSize.height);
43361         }
43362         if(this.closeBtn){
43363             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43364         }
43365         this.updateTitle(panel.getTitle());
43366         if(this.tabs){
43367             this.fireEvent("invalidated", this);
43368         }
43369         this.fireEvent("panelactivated", this, panel);
43370     },
43371
43372     /**
43373      * Shows the specified panel.
43374      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43375      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43376      */
43377     showPanel : function(panel){
43378         if(panel = this.getPanel(panel)){
43379             if(this.tabs){
43380                 var tab = this.tabs.getTab(panel.getEl().id);
43381                 if(tab.isHidden()){
43382                     this.tabs.unhideTab(tab.id);
43383                 }
43384                 tab.activate();
43385             }else{
43386                 this.setActivePanel(panel);
43387             }
43388         }
43389         return panel;
43390     },
43391
43392     /**
43393      * Get the active panel for this region.
43394      * @return {Roo.ContentPanel} The active panel or null
43395      */
43396     getActivePanel : function(){
43397         return this.activePanel;
43398     },
43399
43400     validateVisibility : function(){
43401         if(this.panels.getCount() < 1){
43402             this.updateTitle("&#160;");
43403             this.closeBtn.hide();
43404             this.hide();
43405         }else{
43406             if(!this.isVisible()){
43407                 this.show();
43408             }
43409         }
43410     },
43411
43412     /**
43413      * Adds the passed ContentPanel(s) to this region.
43414      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43415      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43416      */
43417     add : function(panel){
43418         if(arguments.length > 1){
43419             for(var i = 0, len = arguments.length; i < len; i++) {
43420                 this.add(arguments[i]);
43421             }
43422             return null;
43423         }
43424         if(this.hasPanel(panel)){
43425             this.showPanel(panel);
43426             return panel;
43427         }
43428         panel.setRegion(this);
43429         this.panels.add(panel);
43430         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43431             this.bodyEl.dom.appendChild(panel.getEl().dom);
43432             if(panel.background !== true){
43433                 this.setActivePanel(panel);
43434             }
43435             this.fireEvent("paneladded", this, panel);
43436             return panel;
43437         }
43438         if(!this.tabs){
43439             this.initTabs();
43440         }else{
43441             this.initPanelAsTab(panel);
43442         }
43443         if(panel.background !== true){
43444             this.tabs.activate(panel.getEl().id);
43445         }
43446         this.fireEvent("paneladded", this, panel);
43447         return panel;
43448     },
43449
43450     /**
43451      * Hides the tab for the specified panel.
43452      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43453      */
43454     hidePanel : function(panel){
43455         if(this.tabs && (panel = this.getPanel(panel))){
43456             this.tabs.hideTab(panel.getEl().id);
43457         }
43458     },
43459
43460     /**
43461      * Unhides the tab for a previously hidden panel.
43462      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43463      */
43464     unhidePanel : function(panel){
43465         if(this.tabs && (panel = this.getPanel(panel))){
43466             this.tabs.unhideTab(panel.getEl().id);
43467         }
43468     },
43469
43470     clearPanels : function(){
43471         while(this.panels.getCount() > 0){
43472              this.remove(this.panels.first());
43473         }
43474     },
43475
43476     /**
43477      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43478      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43479      * @param {Boolean} preservePanel Overrides the config preservePanel option
43480      * @return {Roo.ContentPanel} The panel that was removed
43481      */
43482     remove : function(panel, preservePanel){
43483         panel = this.getPanel(panel);
43484         if(!panel){
43485             return null;
43486         }
43487         var e = {};
43488         this.fireEvent("beforeremove", this, panel, e);
43489         if(e.cancel === true){
43490             return null;
43491         }
43492         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43493         var panelId = panel.getId();
43494         this.panels.removeKey(panelId);
43495         if(preservePanel){
43496             document.body.appendChild(panel.getEl().dom);
43497         }
43498         if(this.tabs){
43499             this.tabs.removeTab(panel.getEl().id);
43500         }else if (!preservePanel){
43501             this.bodyEl.dom.removeChild(panel.getEl().dom);
43502         }
43503         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43504             var p = this.panels.first();
43505             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43506             tempEl.appendChild(p.getEl().dom);
43507             this.bodyEl.update("");
43508             this.bodyEl.dom.appendChild(p.getEl().dom);
43509             tempEl = null;
43510             this.updateTitle(p.getTitle());
43511             this.tabs = null;
43512             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43513             this.setActivePanel(p);
43514         }
43515         panel.setRegion(null);
43516         if(this.activePanel == panel){
43517             this.activePanel = null;
43518         }
43519         if(this.config.autoDestroy !== false && preservePanel !== true){
43520             try{panel.destroy();}catch(e){}
43521         }
43522         this.fireEvent("panelremoved", this, panel);
43523         return panel;
43524     },
43525
43526     /**
43527      * Returns the TabPanel component used by this region
43528      * @return {Roo.TabPanel}
43529      */
43530     getTabs : function(){
43531         return this.tabs;
43532     },
43533
43534     createTool : function(parentEl, className){
43535         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43536             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43537         btn.addClassOnOver("x-layout-tools-button-over");
43538         return btn;
43539     }
43540 });/*
43541  * Based on:
43542  * Ext JS Library 1.1.1
43543  * Copyright(c) 2006-2007, Ext JS, LLC.
43544  *
43545  * Originally Released Under LGPL - original licence link has changed is not relivant.
43546  *
43547  * Fork - LGPL
43548  * <script type="text/javascript">
43549  */
43550  
43551
43552
43553 /**
43554  * @class Roo.SplitLayoutRegion
43555  * @extends Roo.LayoutRegion
43556  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43557  */
43558 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43559     this.cursor = cursor;
43560     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43561 };
43562
43563 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43564     splitTip : "Drag to resize.",
43565     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43566     useSplitTips : false,
43567
43568     applyConfig : function(config){
43569         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43570         if(config.split){
43571             if(!this.split){
43572                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43573                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43574                 /** The SplitBar for this region 
43575                 * @type Roo.SplitBar */
43576                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43577                 this.split.on("moved", this.onSplitMove, this);
43578                 this.split.useShim = config.useShim === true;
43579                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43580                 if(this.useSplitTips){
43581                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43582                 }
43583                 if(config.collapsible){
43584                     this.split.el.on("dblclick", this.collapse,  this);
43585                 }
43586             }
43587             if(typeof config.minSize != "undefined"){
43588                 this.split.minSize = config.minSize;
43589             }
43590             if(typeof config.maxSize != "undefined"){
43591                 this.split.maxSize = config.maxSize;
43592             }
43593             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43594                 this.hideSplitter();
43595             }
43596         }
43597     },
43598
43599     getHMaxSize : function(){
43600          var cmax = this.config.maxSize || 10000;
43601          var center = this.mgr.getRegion("center");
43602          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43603     },
43604
43605     getVMaxSize : function(){
43606          var cmax = this.config.maxSize || 10000;
43607          var center = this.mgr.getRegion("center");
43608          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43609     },
43610
43611     onSplitMove : function(split, newSize){
43612         this.fireEvent("resized", this, newSize);
43613     },
43614     
43615     /** 
43616      * Returns the {@link Roo.SplitBar} for this region.
43617      * @return {Roo.SplitBar}
43618      */
43619     getSplitBar : function(){
43620         return this.split;
43621     },
43622     
43623     hide : function(){
43624         this.hideSplitter();
43625         Roo.SplitLayoutRegion.superclass.hide.call(this);
43626     },
43627
43628     hideSplitter : function(){
43629         if(this.split){
43630             this.split.el.setLocation(-2000,-2000);
43631             this.split.el.hide();
43632         }
43633     },
43634
43635     show : function(){
43636         if(this.split){
43637             this.split.el.show();
43638         }
43639         Roo.SplitLayoutRegion.superclass.show.call(this);
43640     },
43641     
43642     beforeSlide: function(){
43643         if(Roo.isGecko){// firefox overflow auto bug workaround
43644             this.bodyEl.clip();
43645             if(this.tabs) this.tabs.bodyEl.clip();
43646             if(this.activePanel){
43647                 this.activePanel.getEl().clip();
43648                 
43649                 if(this.activePanel.beforeSlide){
43650                     this.activePanel.beforeSlide();
43651                 }
43652             }
43653         }
43654     },
43655     
43656     afterSlide : function(){
43657         if(Roo.isGecko){// firefox overflow auto bug workaround
43658             this.bodyEl.unclip();
43659             if(this.tabs) this.tabs.bodyEl.unclip();
43660             if(this.activePanel){
43661                 this.activePanel.getEl().unclip();
43662                 if(this.activePanel.afterSlide){
43663                     this.activePanel.afterSlide();
43664                 }
43665             }
43666         }
43667     },
43668
43669     initAutoHide : function(){
43670         if(this.autoHide !== false){
43671             if(!this.autoHideHd){
43672                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43673                 this.autoHideHd = {
43674                     "mouseout": function(e){
43675                         if(!e.within(this.el, true)){
43676                             st.delay(500);
43677                         }
43678                     },
43679                     "mouseover" : function(e){
43680                         st.cancel();
43681                     },
43682                     scope : this
43683                 };
43684             }
43685             this.el.on(this.autoHideHd);
43686         }
43687     },
43688
43689     clearAutoHide : function(){
43690         if(this.autoHide !== false){
43691             this.el.un("mouseout", this.autoHideHd.mouseout);
43692             this.el.un("mouseover", this.autoHideHd.mouseover);
43693         }
43694     },
43695
43696     clearMonitor : function(){
43697         Roo.get(document).un("click", this.slideInIf, this);
43698     },
43699
43700     // these names are backwards but not changed for compat
43701     slideOut : function(){
43702         if(this.isSlid || this.el.hasActiveFx()){
43703             return;
43704         }
43705         this.isSlid = true;
43706         if(this.collapseBtn){
43707             this.collapseBtn.hide();
43708         }
43709         this.closeBtnState = this.closeBtn.getStyle('display');
43710         this.closeBtn.hide();
43711         if(this.stickBtn){
43712             this.stickBtn.show();
43713         }
43714         this.el.show();
43715         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43716         this.beforeSlide();
43717         this.el.setStyle("z-index", 10001);
43718         this.el.slideIn(this.getSlideAnchor(), {
43719             callback: function(){
43720                 this.afterSlide();
43721                 this.initAutoHide();
43722                 Roo.get(document).on("click", this.slideInIf, this);
43723                 this.fireEvent("slideshow", this);
43724             },
43725             scope: this,
43726             block: true
43727         });
43728     },
43729
43730     afterSlideIn : function(){
43731         this.clearAutoHide();
43732         this.isSlid = false;
43733         this.clearMonitor();
43734         this.el.setStyle("z-index", "");
43735         if(this.collapseBtn){
43736             this.collapseBtn.show();
43737         }
43738         this.closeBtn.setStyle('display', this.closeBtnState);
43739         if(this.stickBtn){
43740             this.stickBtn.hide();
43741         }
43742         this.fireEvent("slidehide", this);
43743     },
43744
43745     slideIn : function(cb){
43746         if(!this.isSlid || this.el.hasActiveFx()){
43747             Roo.callback(cb);
43748             return;
43749         }
43750         this.isSlid = false;
43751         this.beforeSlide();
43752         this.el.slideOut(this.getSlideAnchor(), {
43753             callback: function(){
43754                 this.el.setLeftTop(-10000, -10000);
43755                 this.afterSlide();
43756                 this.afterSlideIn();
43757                 Roo.callback(cb);
43758             },
43759             scope: this,
43760             block: true
43761         });
43762     },
43763     
43764     slideInIf : function(e){
43765         if(!e.within(this.el)){
43766             this.slideIn();
43767         }
43768     },
43769
43770     animateCollapse : function(){
43771         this.beforeSlide();
43772         this.el.setStyle("z-index", 20000);
43773         var anchor = this.getSlideAnchor();
43774         this.el.slideOut(anchor, {
43775             callback : function(){
43776                 this.el.setStyle("z-index", "");
43777                 this.collapsedEl.slideIn(anchor, {duration:.3});
43778                 this.afterSlide();
43779                 this.el.setLocation(-10000,-10000);
43780                 this.el.hide();
43781                 this.fireEvent("collapsed", this);
43782             },
43783             scope: this,
43784             block: true
43785         });
43786     },
43787
43788     animateExpand : function(){
43789         this.beforeSlide();
43790         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
43791         this.el.setStyle("z-index", 20000);
43792         this.collapsedEl.hide({
43793             duration:.1
43794         });
43795         this.el.slideIn(this.getSlideAnchor(), {
43796             callback : function(){
43797                 this.el.setStyle("z-index", "");
43798                 this.afterSlide();
43799                 if(this.split){
43800                     this.split.el.show();
43801                 }
43802                 this.fireEvent("invalidated", this);
43803                 this.fireEvent("expanded", this);
43804             },
43805             scope: this,
43806             block: true
43807         });
43808     },
43809
43810     anchors : {
43811         "west" : "left",
43812         "east" : "right",
43813         "north" : "top",
43814         "south" : "bottom"
43815     },
43816
43817     sanchors : {
43818         "west" : "l",
43819         "east" : "r",
43820         "north" : "t",
43821         "south" : "b"
43822     },
43823
43824     canchors : {
43825         "west" : "tl-tr",
43826         "east" : "tr-tl",
43827         "north" : "tl-bl",
43828         "south" : "bl-tl"
43829     },
43830
43831     getAnchor : function(){
43832         return this.anchors[this.position];
43833     },
43834
43835     getCollapseAnchor : function(){
43836         return this.canchors[this.position];
43837     },
43838
43839     getSlideAnchor : function(){
43840         return this.sanchors[this.position];
43841     },
43842
43843     getAlignAdj : function(){
43844         var cm = this.cmargins;
43845         switch(this.position){
43846             case "west":
43847                 return [0, 0];
43848             break;
43849             case "east":
43850                 return [0, 0];
43851             break;
43852             case "north":
43853                 return [0, 0];
43854             break;
43855             case "south":
43856                 return [0, 0];
43857             break;
43858         }
43859     },
43860
43861     getExpandAdj : function(){
43862         var c = this.collapsedEl, cm = this.cmargins;
43863         switch(this.position){
43864             case "west":
43865                 return [-(cm.right+c.getWidth()+cm.left), 0];
43866             break;
43867             case "east":
43868                 return [cm.right+c.getWidth()+cm.left, 0];
43869             break;
43870             case "north":
43871                 return [0, -(cm.top+cm.bottom+c.getHeight())];
43872             break;
43873             case "south":
43874                 return [0, cm.top+cm.bottom+c.getHeight()];
43875             break;
43876         }
43877     }
43878 });/*
43879  * Based on:
43880  * Ext JS Library 1.1.1
43881  * Copyright(c) 2006-2007, Ext JS, LLC.
43882  *
43883  * Originally Released Under LGPL - original licence link has changed is not relivant.
43884  *
43885  * Fork - LGPL
43886  * <script type="text/javascript">
43887  */
43888 /*
43889  * These classes are private internal classes
43890  */
43891 Roo.CenterLayoutRegion = function(mgr, config){
43892     Roo.LayoutRegion.call(this, mgr, config, "center");
43893     this.visible = true;
43894     this.minWidth = config.minWidth || 20;
43895     this.minHeight = config.minHeight || 20;
43896 };
43897
43898 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
43899     hide : function(){
43900         // center panel can't be hidden
43901     },
43902     
43903     show : function(){
43904         // center panel can't be hidden
43905     },
43906     
43907     getMinWidth: function(){
43908         return this.minWidth;
43909     },
43910     
43911     getMinHeight: function(){
43912         return this.minHeight;
43913     }
43914 });
43915
43916
43917 Roo.NorthLayoutRegion = function(mgr, config){
43918     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
43919     if(this.split){
43920         this.split.placement = Roo.SplitBar.TOP;
43921         this.split.orientation = Roo.SplitBar.VERTICAL;
43922         this.split.el.addClass("x-layout-split-v");
43923     }
43924     var size = config.initialSize || config.height;
43925     if(typeof size != "undefined"){
43926         this.el.setHeight(size);
43927     }
43928 };
43929 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
43930     orientation: Roo.SplitBar.VERTICAL,
43931     getBox : function(){
43932         if(this.collapsed){
43933             return this.collapsedEl.getBox();
43934         }
43935         var box = this.el.getBox();
43936         if(this.split){
43937             box.height += this.split.el.getHeight();
43938         }
43939         return box;
43940     },
43941     
43942     updateBox : function(box){
43943         if(this.split && !this.collapsed){
43944             box.height -= this.split.el.getHeight();
43945             this.split.el.setLeft(box.x);
43946             this.split.el.setTop(box.y+box.height);
43947             this.split.el.setWidth(box.width);
43948         }
43949         if(this.collapsed){
43950             this.updateBody(box.width, null);
43951         }
43952         Roo.LayoutRegion.prototype.updateBox.call(this, box);
43953     }
43954 });
43955
43956 Roo.SouthLayoutRegion = function(mgr, config){
43957     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
43958     if(this.split){
43959         this.split.placement = Roo.SplitBar.BOTTOM;
43960         this.split.orientation = Roo.SplitBar.VERTICAL;
43961         this.split.el.addClass("x-layout-split-v");
43962     }
43963     var size = config.initialSize || config.height;
43964     if(typeof size != "undefined"){
43965         this.el.setHeight(size);
43966     }
43967 };
43968 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
43969     orientation: Roo.SplitBar.VERTICAL,
43970     getBox : function(){
43971         if(this.collapsed){
43972             return this.collapsedEl.getBox();
43973         }
43974         var box = this.el.getBox();
43975         if(this.split){
43976             var sh = this.split.el.getHeight();
43977             box.height += sh;
43978             box.y -= sh;
43979         }
43980         return box;
43981     },
43982     
43983     updateBox : function(box){
43984         if(this.split && !this.collapsed){
43985             var sh = this.split.el.getHeight();
43986             box.height -= sh;
43987             box.y += sh;
43988             this.split.el.setLeft(box.x);
43989             this.split.el.setTop(box.y-sh);
43990             this.split.el.setWidth(box.width);
43991         }
43992         if(this.collapsed){
43993             this.updateBody(box.width, null);
43994         }
43995         Roo.LayoutRegion.prototype.updateBox.call(this, box);
43996     }
43997 });
43998
43999 Roo.EastLayoutRegion = function(mgr, config){
44000     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44001     if(this.split){
44002         this.split.placement = Roo.SplitBar.RIGHT;
44003         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44004         this.split.el.addClass("x-layout-split-h");
44005     }
44006     var size = config.initialSize || config.width;
44007     if(typeof size != "undefined"){
44008         this.el.setWidth(size);
44009     }
44010 };
44011 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44012     orientation: Roo.SplitBar.HORIZONTAL,
44013     getBox : function(){
44014         if(this.collapsed){
44015             return this.collapsedEl.getBox();
44016         }
44017         var box = this.el.getBox();
44018         if(this.split){
44019             var sw = this.split.el.getWidth();
44020             box.width += sw;
44021             box.x -= sw;
44022         }
44023         return box;
44024     },
44025
44026     updateBox : function(box){
44027         if(this.split && !this.collapsed){
44028             var sw = this.split.el.getWidth();
44029             box.width -= sw;
44030             this.split.el.setLeft(box.x);
44031             this.split.el.setTop(box.y);
44032             this.split.el.setHeight(box.height);
44033             box.x += sw;
44034         }
44035         if(this.collapsed){
44036             this.updateBody(null, box.height);
44037         }
44038         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44039     }
44040 });
44041
44042 Roo.WestLayoutRegion = function(mgr, config){
44043     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44044     if(this.split){
44045         this.split.placement = Roo.SplitBar.LEFT;
44046         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44047         this.split.el.addClass("x-layout-split-h");
44048     }
44049     var size = config.initialSize || config.width;
44050     if(typeof size != "undefined"){
44051         this.el.setWidth(size);
44052     }
44053 };
44054 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44055     orientation: Roo.SplitBar.HORIZONTAL,
44056     getBox : function(){
44057         if(this.collapsed){
44058             return this.collapsedEl.getBox();
44059         }
44060         var box = this.el.getBox();
44061         if(this.split){
44062             box.width += this.split.el.getWidth();
44063         }
44064         return box;
44065     },
44066     
44067     updateBox : function(box){
44068         if(this.split && !this.collapsed){
44069             var sw = this.split.el.getWidth();
44070             box.width -= sw;
44071             this.split.el.setLeft(box.x+box.width);
44072             this.split.el.setTop(box.y);
44073             this.split.el.setHeight(box.height);
44074         }
44075         if(this.collapsed){
44076             this.updateBody(null, box.height);
44077         }
44078         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44079     }
44080 });
44081 /*
44082  * Based on:
44083  * Ext JS Library 1.1.1
44084  * Copyright(c) 2006-2007, Ext JS, LLC.
44085  *
44086  * Originally Released Under LGPL - original licence link has changed is not relivant.
44087  *
44088  * Fork - LGPL
44089  * <script type="text/javascript">
44090  */
44091  
44092  
44093 /*
44094  * Private internal class for reading and applying state
44095  */
44096 Roo.LayoutStateManager = function(layout){
44097      // default empty state
44098      this.state = {
44099         north: {},
44100         south: {},
44101         east: {},
44102         west: {}       
44103     };
44104 };
44105
44106 Roo.LayoutStateManager.prototype = {
44107     init : function(layout, provider){
44108         this.provider = provider;
44109         var state = provider.get(layout.id+"-layout-state");
44110         if(state){
44111             var wasUpdating = layout.isUpdating();
44112             if(!wasUpdating){
44113                 layout.beginUpdate();
44114             }
44115             for(var key in state){
44116                 if(typeof state[key] != "function"){
44117                     var rstate = state[key];
44118                     var r = layout.getRegion(key);
44119                     if(r && rstate){
44120                         if(rstate.size){
44121                             r.resizeTo(rstate.size);
44122                         }
44123                         if(rstate.collapsed == true){
44124                             r.collapse(true);
44125                         }else{
44126                             r.expand(null, true);
44127                         }
44128                     }
44129                 }
44130             }
44131             if(!wasUpdating){
44132                 layout.endUpdate();
44133             }
44134             this.state = state; 
44135         }
44136         this.layout = layout;
44137         layout.on("regionresized", this.onRegionResized, this);
44138         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44139         layout.on("regionexpanded", this.onRegionExpanded, this);
44140     },
44141     
44142     storeState : function(){
44143         this.provider.set(this.layout.id+"-layout-state", this.state);
44144     },
44145     
44146     onRegionResized : function(region, newSize){
44147         this.state[region.getPosition()].size = newSize;
44148         this.storeState();
44149     },
44150     
44151     onRegionCollapsed : function(region){
44152         this.state[region.getPosition()].collapsed = true;
44153         this.storeState();
44154     },
44155     
44156     onRegionExpanded : function(region){
44157         this.state[region.getPosition()].collapsed = false;
44158         this.storeState();
44159     }
44160 };/*
44161  * Based on:
44162  * Ext JS Library 1.1.1
44163  * Copyright(c) 2006-2007, Ext JS, LLC.
44164  *
44165  * Originally Released Under LGPL - original licence link has changed is not relivant.
44166  *
44167  * Fork - LGPL
44168  * <script type="text/javascript">
44169  */
44170 /**
44171  * @class Roo.ContentPanel
44172  * @extends Roo.util.Observable
44173  * A basic ContentPanel element.
44174  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44175  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44176  * @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
44177  * @cfg {Boolean} closable True if the panel can be closed/removed
44178  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44179  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44180  * @cfg {Toolbar} toolbar A toolbar for this panel
44181  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44182  * @cfg {String} title The title for this panel
44183  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44184  * @cfg {String} url Calls {@link #setUrl} with this value
44185  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44186  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44187  * @constructor
44188  * Create a new ContentPanel.
44189  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44190  * @param {String/Object} config A string to set only the title or a config object
44191  * @param {String} content (optional) Set the HTML content for this panel
44192  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44193  */
44194 Roo.ContentPanel = function(el, config, content){
44195     
44196      
44197     /*
44198     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44199         config = el;
44200         el = Roo.id();
44201     }
44202     if (config && config.parentLayout) { 
44203         el = config.parentLayout.el.createChild(); 
44204     }
44205     */
44206     if(el.autoCreate){ // xtype is available if this is called from factory
44207         config = el;
44208         el = Roo.id();
44209     }
44210     this.el = Roo.get(el);
44211     if(!this.el && config && config.autoCreate){
44212         if(typeof config.autoCreate == "object"){
44213             if(!config.autoCreate.id){
44214                 config.autoCreate.id = config.id||el;
44215             }
44216             this.el = Roo.DomHelper.append(document.body,
44217                         config.autoCreate, true);
44218         }else{
44219             this.el = Roo.DomHelper.append(document.body,
44220                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44221         }
44222     }
44223     this.closable = false;
44224     this.loaded = false;
44225     this.active = false;
44226     if(typeof config == "string"){
44227         this.title = config;
44228     }else{
44229         Roo.apply(this, config);
44230     }
44231     
44232     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44233         this.wrapEl = this.el.wrap();    
44234         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44235         
44236     }
44237     
44238     
44239     
44240     if(this.resizeEl){
44241         this.resizeEl = Roo.get(this.resizeEl, true);
44242     }else{
44243         this.resizeEl = this.el;
44244     }
44245     this.addEvents({
44246         /**
44247          * @event activate
44248          * Fires when this panel is activated. 
44249          * @param {Roo.ContentPanel} this
44250          */
44251         "activate" : true,
44252         /**
44253          * @event deactivate
44254          * Fires when this panel is activated. 
44255          * @param {Roo.ContentPanel} this
44256          */
44257         "deactivate" : true,
44258
44259         /**
44260          * @event resize
44261          * Fires when this panel is resized if fitToFrame is true.
44262          * @param {Roo.ContentPanel} this
44263          * @param {Number} width The width after any component adjustments
44264          * @param {Number} height The height after any component adjustments
44265          */
44266         "resize" : true
44267     });
44268     if(this.autoScroll){
44269         this.resizeEl.setStyle("overflow", "auto");
44270     }
44271     content = content || this.content;
44272     if(content){
44273         this.setContent(content);
44274     }
44275     if(config && config.url){
44276         this.setUrl(this.url, this.params, this.loadOnce);
44277     }
44278     
44279     
44280     
44281     Roo.ContentPanel.superclass.constructor.call(this);
44282 };
44283
44284 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44285     tabTip:'',
44286     setRegion : function(region){
44287         this.region = region;
44288         if(region){
44289            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44290         }else{
44291            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44292         } 
44293     },
44294     
44295     /**
44296      * Returns the toolbar for this Panel if one was configured. 
44297      * @return {Roo.Toolbar} 
44298      */
44299     getToolbar : function(){
44300         return this.toolbar;
44301     },
44302     
44303     setActiveState : function(active){
44304         this.active = active;
44305         if(!active){
44306             this.fireEvent("deactivate", this);
44307         }else{
44308             this.fireEvent("activate", this);
44309         }
44310     },
44311     /**
44312      * Updates this panel's element
44313      * @param {String} content The new content
44314      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44315     */
44316     setContent : function(content, loadScripts){
44317         this.el.update(content, loadScripts);
44318     },
44319
44320     ignoreResize : function(w, h){
44321         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44322             return true;
44323         }else{
44324             this.lastSize = {width: w, height: h};
44325             return false;
44326         }
44327     },
44328     /**
44329      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44330      * @return {Roo.UpdateManager} The UpdateManager
44331      */
44332     getUpdateManager : function(){
44333         return this.el.getUpdateManager();
44334     },
44335      /**
44336      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44337      * @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:
44338 <pre><code>
44339 panel.load({
44340     url: "your-url.php",
44341     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44342     callback: yourFunction,
44343     scope: yourObject, //(optional scope)
44344     discardUrl: false,
44345     nocache: false,
44346     text: "Loading...",
44347     timeout: 30,
44348     scripts: false
44349 });
44350 </code></pre>
44351      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44352      * 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.
44353      * @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}
44354      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44355      * @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.
44356      * @return {Roo.ContentPanel} this
44357      */
44358     load : function(){
44359         var um = this.el.getUpdateManager();
44360         um.update.apply(um, arguments);
44361         return this;
44362     },
44363
44364
44365     /**
44366      * 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.
44367      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44368      * @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)
44369      * @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)
44370      * @return {Roo.UpdateManager} The UpdateManager
44371      */
44372     setUrl : function(url, params, loadOnce){
44373         if(this.refreshDelegate){
44374             this.removeListener("activate", this.refreshDelegate);
44375         }
44376         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44377         this.on("activate", this.refreshDelegate);
44378         return this.el.getUpdateManager();
44379     },
44380     
44381     _handleRefresh : function(url, params, loadOnce){
44382         if(!loadOnce || !this.loaded){
44383             var updater = this.el.getUpdateManager();
44384             updater.update(url, params, this._setLoaded.createDelegate(this));
44385         }
44386     },
44387     
44388     _setLoaded : function(){
44389         this.loaded = true;
44390     }, 
44391     
44392     /**
44393      * Returns this panel's id
44394      * @return {String} 
44395      */
44396     getId : function(){
44397         return this.el.id;
44398     },
44399     
44400     /** 
44401      * Returns this panel's element - used by regiosn to add.
44402      * @return {Roo.Element} 
44403      */
44404     getEl : function(){
44405         return this.wrapEl || this.el;
44406     },
44407     
44408     adjustForComponents : function(width, height){
44409         if(this.resizeEl != this.el){
44410             width -= this.el.getFrameWidth('lr');
44411             height -= this.el.getFrameWidth('tb');
44412         }
44413         if(this.toolbar){
44414             var te = this.toolbar.getEl();
44415             height -= te.getHeight();
44416             te.setWidth(width);
44417         }
44418         if(this.adjustments){
44419             width += this.adjustments[0];
44420             height += this.adjustments[1];
44421         }
44422         return {"width": width, "height": height};
44423     },
44424     
44425     setSize : function(width, height){
44426         if(this.fitToFrame && !this.ignoreResize(width, height)){
44427             if(this.fitContainer && this.resizeEl != this.el){
44428                 this.el.setSize(width, height);
44429             }
44430             var size = this.adjustForComponents(width, height);
44431             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44432             this.fireEvent('resize', this, size.width, size.height);
44433         }
44434     },
44435     
44436     /**
44437      * Returns this panel's title
44438      * @return {String} 
44439      */
44440     getTitle : function(){
44441         return this.title;
44442     },
44443     
44444     /**
44445      * Set this panel's title
44446      * @param {String} title
44447      */
44448     setTitle : function(title){
44449         this.title = title;
44450         if(this.region){
44451             this.region.updatePanelTitle(this, title);
44452         }
44453     },
44454     
44455     /**
44456      * Returns true is this panel was configured to be closable
44457      * @return {Boolean} 
44458      */
44459     isClosable : function(){
44460         return this.closable;
44461     },
44462     
44463     beforeSlide : function(){
44464         this.el.clip();
44465         this.resizeEl.clip();
44466     },
44467     
44468     afterSlide : function(){
44469         this.el.unclip();
44470         this.resizeEl.unclip();
44471     },
44472     
44473     /**
44474      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44475      *   Will fail silently if the {@link #setUrl} method has not been called.
44476      *   This does not activate the panel, just updates its content.
44477      */
44478     refresh : function(){
44479         if(this.refreshDelegate){
44480            this.loaded = false;
44481            this.refreshDelegate();
44482         }
44483     },
44484     
44485     /**
44486      * Destroys this panel
44487      */
44488     destroy : function(){
44489         this.el.removeAllListeners();
44490         var tempEl = document.createElement("span");
44491         tempEl.appendChild(this.el.dom);
44492         tempEl.innerHTML = "";
44493         this.el.remove();
44494         this.el = null;
44495     },
44496     
44497       /**
44498      * Adds a xtype elements to the panel - currently only supports Forms.
44499      * <pre><code>
44500
44501 layout.addxtype({
44502        xtype : 'Form',
44503        items: [ .... ]
44504    }
44505 );
44506
44507 </code></pre>
44508      * @param {Object} cfg Xtype definition of item to add.
44509      */
44510     
44511     addxtype : function(cfg) {
44512         // add form..
44513         if (!cfg.xtype.match(/^Form$/)) {
44514             return false;
44515         }
44516         var el = this.el.createChild();
44517         this.form = new  Roo.form.Form(cfg);
44518         
44519         if ( this.form.allItems.length) this.form.render(el.dom);
44520         return this.form;
44521         
44522     }
44523 });
44524
44525 /**
44526  * @class Roo.GridPanel
44527  * @extends Roo.ContentPanel
44528  * @constructor
44529  * Create a new GridPanel.
44530  * @param {Roo.grid.Grid} grid The grid for this panel
44531  * @param {String/Object} config A string to set only the panel's title, or a config object
44532  */
44533 Roo.GridPanel = function(grid, config){
44534     
44535   
44536     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44537         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44538         
44539     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44540     
44541     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44542     
44543     if(this.toolbar){
44544         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44545     }
44546     // xtype created footer. - not sure if will work as we normally have to render first..
44547     if (this.footer && !this.footer.el && this.footer.xtype) {
44548         
44549         this.footer.container = this.grid.getView().getFooterPanel(true);
44550         this.footer.dataSource = this.grid.dataSource;
44551         this.footer = Roo.factory(this.footer, Roo);
44552         
44553     }
44554     
44555     grid.monitorWindowResize = false; // turn off autosizing
44556     grid.autoHeight = false;
44557     grid.autoWidth = false;
44558     this.grid = grid;
44559     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44560 };
44561
44562 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44563     getId : function(){
44564         return this.grid.id;
44565     },
44566     
44567     /**
44568      * Returns the grid for this panel
44569      * @return {Roo.grid.Grid} 
44570      */
44571     getGrid : function(){
44572         return this.grid;    
44573     },
44574     
44575     setSize : function(width, height){
44576         if(!this.ignoreResize(width, height)){
44577             var grid = this.grid;
44578             var size = this.adjustForComponents(width, height);
44579             grid.getGridEl().setSize(size.width, size.height);
44580             grid.autoSize();
44581         }
44582     },
44583     
44584     beforeSlide : function(){
44585         this.grid.getView().scroller.clip();
44586     },
44587     
44588     afterSlide : function(){
44589         this.grid.getView().scroller.unclip();
44590     },
44591     
44592     destroy : function(){
44593         this.grid.destroy();
44594         delete this.grid;
44595         Roo.GridPanel.superclass.destroy.call(this); 
44596     }
44597 });
44598
44599
44600 /**
44601  * @class Roo.NestedLayoutPanel
44602  * @extends Roo.ContentPanel
44603  * @constructor
44604  * Create a new NestedLayoutPanel.
44605  * 
44606  * 
44607  * @param {Roo.BorderLayout} layout The layout for this panel
44608  * @param {String/Object} config A string to set only the title or a config object
44609  */
44610 Roo.NestedLayoutPanel = function(layout, config)
44611 {
44612     // construct with only one argument..
44613     /* FIXME - implement nicer consturctors
44614     if (layout.layout) {
44615         config = layout;
44616         layout = config.layout;
44617         delete config.layout;
44618     }
44619     if (layout.xtype && !layout.getEl) {
44620         // then layout needs constructing..
44621         layout = Roo.factory(layout, Roo);
44622     }
44623     */
44624     
44625     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44626     
44627     layout.monitorWindowResize = false; // turn off autosizing
44628     this.layout = layout;
44629     this.layout.getEl().addClass("x-layout-nested-layout");
44630     
44631     
44632     
44633 };
44634
44635 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44636
44637     setSize : function(width, height){
44638         if(!this.ignoreResize(width, height)){
44639             var size = this.adjustForComponents(width, height);
44640             var el = this.layout.getEl();
44641             el.setSize(size.width, size.height);
44642             var touch = el.dom.offsetWidth;
44643             this.layout.layout();
44644             // ie requires a double layout on the first pass
44645             if(Roo.isIE && !this.initialized){
44646                 this.initialized = true;
44647                 this.layout.layout();
44648             }
44649         }
44650     },
44651     
44652     // activate all subpanels if not currently active..
44653     
44654     setActiveState : function(active){
44655         this.active = active;
44656         if(!active){
44657             this.fireEvent("deactivate", this);
44658             return;
44659         }
44660         
44661         this.fireEvent("activate", this);
44662         // not sure if this should happen before or after..
44663         if (!this.layout) {
44664             return; // should not happen..
44665         }
44666         var reg = false;
44667         for (var r in this.layout.regions) {
44668             reg = this.layout.getRegion(r);
44669             if (reg.getActivePanel()) {
44670                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44671                 reg.setActivePanel(reg.getActivePanel());
44672                 continue;
44673             }
44674             if (!reg.panels.length) {
44675                 continue;
44676             }
44677             reg.showPanel(reg.getPanel(0));
44678         }
44679         
44680         
44681         
44682         
44683     },
44684     
44685     /**
44686      * Returns the nested BorderLayout for this panel
44687      * @return {Roo.BorderLayout} 
44688      */
44689     getLayout : function(){
44690         return this.layout;
44691     },
44692     
44693      /**
44694      * Adds a xtype elements to the layout of the nested panel
44695      * <pre><code>
44696
44697 panel.addxtype({
44698        xtype : 'ContentPanel',
44699        region: 'west',
44700        items: [ .... ]
44701    }
44702 );
44703
44704 panel.addxtype({
44705         xtype : 'NestedLayoutPanel',
44706         region: 'west',
44707         layout: {
44708            center: { },
44709            west: { }   
44710         },
44711         items : [ ... list of content panels or nested layout panels.. ]
44712    }
44713 );
44714 </code></pre>
44715      * @param {Object} cfg Xtype definition of item to add.
44716      */
44717     addxtype : function(cfg) {
44718         return this.layout.addxtype(cfg);
44719     
44720     }
44721 });
44722
44723 Roo.ScrollPanel = function(el, config, content){
44724     config = config || {};
44725     config.fitToFrame = true;
44726     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
44727     
44728     this.el.dom.style.overflow = "hidden";
44729     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
44730     this.el.removeClass("x-layout-inactive-content");
44731     this.el.on("mousewheel", this.onWheel, this);
44732
44733     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
44734     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
44735     up.unselectable(); down.unselectable();
44736     up.on("click", this.scrollUp, this);
44737     down.on("click", this.scrollDown, this);
44738     up.addClassOnOver("x-scroller-btn-over");
44739     down.addClassOnOver("x-scroller-btn-over");
44740     up.addClassOnClick("x-scroller-btn-click");
44741     down.addClassOnClick("x-scroller-btn-click");
44742     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
44743
44744     this.resizeEl = this.el;
44745     this.el = wrap; this.up = up; this.down = down;
44746 };
44747
44748 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
44749     increment : 100,
44750     wheelIncrement : 5,
44751     scrollUp : function(){
44752         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
44753     },
44754
44755     scrollDown : function(){
44756         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
44757     },
44758
44759     afterScroll : function(){
44760         var el = this.resizeEl;
44761         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
44762         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44763         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44764     },
44765
44766     setSize : function(){
44767         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
44768         this.afterScroll();
44769     },
44770
44771     onWheel : function(e){
44772         var d = e.getWheelDelta();
44773         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
44774         this.afterScroll();
44775         e.stopEvent();
44776     },
44777
44778     setContent : function(content, loadScripts){
44779         this.resizeEl.update(content, loadScripts);
44780     }
44781
44782 });
44783
44784
44785
44786
44787
44788
44789
44790
44791
44792 /**
44793  * @class Roo.TreePanel
44794  * @extends Roo.ContentPanel
44795  * @constructor
44796  * Create a new TreePanel.
44797  * @param {String/Object} config A string to set only the panel's title, or a config object
44798  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
44799  */
44800 Roo.TreePanel = function(config){
44801     var el = config.el;
44802     var tree = config.tree;
44803     delete config.tree; 
44804     delete config.el; // hopefull!
44805     Roo.TreePanel.superclass.constructor.call(this, el, config);
44806     var treeEl = el.createChild();
44807     this.tree = new Roo.tree.TreePanel(treeEl , tree);
44808     //console.log(tree);
44809     this.on('activate', function()
44810     {
44811         if (this.tree.rendered) {
44812             return;
44813         }
44814         //console.log('render tree');
44815         this.tree.render();
44816     });
44817     
44818     this.on('resize',  function (cp, w, h) {
44819             this.tree.innerCt.setWidth(w);
44820             this.tree.innerCt.setHeight(h);
44821             this.tree.innerCt.setStyle('overflow-y', 'auto');
44822     });
44823
44824         
44825     
44826 };
44827
44828 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
44829
44830
44831
44832
44833
44834
44835
44836
44837
44838
44839
44840 /*
44841  * Based on:
44842  * Ext JS Library 1.1.1
44843  * Copyright(c) 2006-2007, Ext JS, LLC.
44844  *
44845  * Originally Released Under LGPL - original licence link has changed is not relivant.
44846  *
44847  * Fork - LGPL
44848  * <script type="text/javascript">
44849  */
44850  
44851
44852 /**
44853  * @class Roo.ReaderLayout
44854  * @extends Roo.BorderLayout
44855  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
44856  * center region containing two nested regions (a top one for a list view and one for item preview below),
44857  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
44858  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
44859  * expedites the setup of the overall layout and regions for this common application style.
44860  * Example:
44861  <pre><code>
44862 var reader = new Roo.ReaderLayout();
44863 var CP = Roo.ContentPanel;  // shortcut for adding
44864
44865 reader.beginUpdate();
44866 reader.add("north", new CP("north", "North"));
44867 reader.add("west", new CP("west", {title: "West"}));
44868 reader.add("east", new CP("east", {title: "East"}));
44869
44870 reader.regions.listView.add(new CP("listView", "List"));
44871 reader.regions.preview.add(new CP("preview", "Preview"));
44872 reader.endUpdate();
44873 </code></pre>
44874 * @constructor
44875 * Create a new ReaderLayout
44876 * @param {Object} config Configuration options
44877 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
44878 * document.body if omitted)
44879 */
44880 Roo.ReaderLayout = function(config, renderTo){
44881     var c = config || {size:{}};
44882     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
44883         north: c.north !== false ? Roo.apply({
44884             split:false,
44885             initialSize: 32,
44886             titlebar: false
44887         }, c.north) : false,
44888         west: c.west !== false ? Roo.apply({
44889             split:true,
44890             initialSize: 200,
44891             minSize: 175,
44892             maxSize: 400,
44893             titlebar: true,
44894             collapsible: true,
44895             animate: true,
44896             margins:{left:5,right:0,bottom:5,top:5},
44897             cmargins:{left:5,right:5,bottom:5,top:5}
44898         }, c.west) : false,
44899         east: c.east !== false ? Roo.apply({
44900             split:true,
44901             initialSize: 200,
44902             minSize: 175,
44903             maxSize: 400,
44904             titlebar: true,
44905             collapsible: true,
44906             animate: true,
44907             margins:{left:0,right:5,bottom:5,top:5},
44908             cmargins:{left:5,right:5,bottom:5,top:5}
44909         }, c.east) : false,
44910         center: Roo.apply({
44911             tabPosition: 'top',
44912             autoScroll:false,
44913             closeOnTab: true,
44914             titlebar:false,
44915             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
44916         }, c.center)
44917     });
44918
44919     this.el.addClass('x-reader');
44920
44921     this.beginUpdate();
44922
44923     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
44924         south: c.preview !== false ? Roo.apply({
44925             split:true,
44926             initialSize: 200,
44927             minSize: 100,
44928             autoScroll:true,
44929             collapsible:true,
44930             titlebar: true,
44931             cmargins:{top:5,left:0, right:0, bottom:0}
44932         }, c.preview) : false,
44933         center: Roo.apply({
44934             autoScroll:false,
44935             titlebar:false,
44936             minHeight:200
44937         }, c.listView)
44938     });
44939     this.add('center', new Roo.NestedLayoutPanel(inner,
44940             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
44941
44942     this.endUpdate();
44943
44944     this.regions.preview = inner.getRegion('south');
44945     this.regions.listView = inner.getRegion('center');
44946 };
44947
44948 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
44949  * Based on:
44950  * Ext JS Library 1.1.1
44951  * Copyright(c) 2006-2007, Ext JS, LLC.
44952  *
44953  * Originally Released Under LGPL - original licence link has changed is not relivant.
44954  *
44955  * Fork - LGPL
44956  * <script type="text/javascript">
44957  */
44958  
44959 /**
44960  * @class Roo.grid.Grid
44961  * @extends Roo.util.Observable
44962  * This class represents the primary interface of a component based grid control.
44963  * <br><br>Usage:<pre><code>
44964  var grid = new Roo.grid.Grid("my-container-id", {
44965      ds: myDataStore,
44966      cm: myColModel,
44967      selModel: mySelectionModel,
44968      autoSizeColumns: true,
44969      monitorWindowResize: false,
44970      trackMouseOver: true
44971  });
44972  // set any options
44973  grid.render();
44974  * </code></pre>
44975  * <b>Common Problems:</b><br/>
44976  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
44977  * element will correct this<br/>
44978  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
44979  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
44980  * are unpredictable.<br/>
44981  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
44982  * grid to calculate dimensions/offsets.<br/>
44983   * @constructor
44984  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
44985  * The container MUST have some type of size defined for the grid to fill. The container will be
44986  * automatically set to position relative if it isn't already.
44987  * @param {Object} config A config object that sets properties on this grid.
44988  */
44989 Roo.grid.Grid = function(container, config){
44990         // initialize the container
44991         this.container = Roo.get(container);
44992         this.container.update("");
44993         this.container.setStyle("overflow", "hidden");
44994     this.container.addClass('x-grid-container');
44995
44996     this.id = this.container.id;
44997
44998     Roo.apply(this, config);
44999     // check and correct shorthanded configs
45000     if(this.ds){
45001         this.dataSource = this.ds;
45002         delete this.ds;
45003     }
45004     if(this.cm){
45005         this.colModel = this.cm;
45006         delete this.cm;
45007     }
45008     if(this.sm){
45009         this.selModel = this.sm;
45010         delete this.sm;
45011     }
45012
45013     if (this.selModel) {
45014         this.selModel = Roo.factory(this.selModel, Roo.grid);
45015         this.sm = this.selModel;
45016         this.sm.xmodule = this.xmodule || false;
45017     }
45018     if (typeof(this.colModel.config) == 'undefined') {
45019         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45020         this.cm = this.colModel;
45021         this.cm.xmodule = this.xmodule || false;
45022     }
45023     if (this.dataSource) {
45024         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45025         this.ds = this.dataSource;
45026         this.ds.xmodule = this.xmodule || false;
45027         
45028     }
45029     
45030     
45031     
45032     if(this.width){
45033         this.container.setWidth(this.width);
45034     }
45035
45036     if(this.height){
45037         this.container.setHeight(this.height);
45038     }
45039     /** @private */
45040         this.addEvents({
45041             // raw events
45042             /**
45043              * @event click
45044              * The raw click event for the entire grid.
45045              * @param {Roo.EventObject} e
45046              */
45047             "click" : true,
45048             /**
45049              * @event dblclick
45050              * The raw dblclick event for the entire grid.
45051              * @param {Roo.EventObject} e
45052              */
45053             "dblclick" : true,
45054             /**
45055              * @event contextmenu
45056              * The raw contextmenu event for the entire grid.
45057              * @param {Roo.EventObject} e
45058              */
45059             "contextmenu" : true,
45060             /**
45061              * @event mousedown
45062              * The raw mousedown event for the entire grid.
45063              * @param {Roo.EventObject} e
45064              */
45065             "mousedown" : true,
45066             /**
45067              * @event mouseup
45068              * The raw mouseup event for the entire grid.
45069              * @param {Roo.EventObject} e
45070              */
45071             "mouseup" : true,
45072             /**
45073              * @event mouseover
45074              * The raw mouseover event for the entire grid.
45075              * @param {Roo.EventObject} e
45076              */
45077             "mouseover" : true,
45078             /**
45079              * @event mouseout
45080              * The raw mouseout event for the entire grid.
45081              * @param {Roo.EventObject} e
45082              */
45083             "mouseout" : true,
45084             /**
45085              * @event keypress
45086              * The raw keypress event for the entire grid.
45087              * @param {Roo.EventObject} e
45088              */
45089             "keypress" : true,
45090             /**
45091              * @event keydown
45092              * The raw keydown event for the entire grid.
45093              * @param {Roo.EventObject} e
45094              */
45095             "keydown" : true,
45096
45097             // custom events
45098
45099             /**
45100              * @event cellclick
45101              * Fires when a cell is clicked
45102              * @param {Grid} this
45103              * @param {Number} rowIndex
45104              * @param {Number} columnIndex
45105              * @param {Roo.EventObject} e
45106              */
45107             "cellclick" : true,
45108             /**
45109              * @event celldblclick
45110              * Fires when a cell is double clicked
45111              * @param {Grid} this
45112              * @param {Number} rowIndex
45113              * @param {Number} columnIndex
45114              * @param {Roo.EventObject} e
45115              */
45116             "celldblclick" : true,
45117             /**
45118              * @event rowclick
45119              * Fires when a row is clicked
45120              * @param {Grid} this
45121              * @param {Number} rowIndex
45122              * @param {Roo.EventObject} e
45123              */
45124             "rowclick" : true,
45125             /**
45126              * @event rowdblclick
45127              * Fires when a row is double clicked
45128              * @param {Grid} this
45129              * @param {Number} rowIndex
45130              * @param {Roo.EventObject} e
45131              */
45132             "rowdblclick" : true,
45133             /**
45134              * @event headerclick
45135              * Fires when a header is clicked
45136              * @param {Grid} this
45137              * @param {Number} columnIndex
45138              * @param {Roo.EventObject} e
45139              */
45140             "headerclick" : true,
45141             /**
45142              * @event headerdblclick
45143              * Fires when a header cell is double clicked
45144              * @param {Grid} this
45145              * @param {Number} columnIndex
45146              * @param {Roo.EventObject} e
45147              */
45148             "headerdblclick" : true,
45149             /**
45150              * @event rowcontextmenu
45151              * Fires when a row is right clicked
45152              * @param {Grid} this
45153              * @param {Number} rowIndex
45154              * @param {Roo.EventObject} e
45155              */
45156             "rowcontextmenu" : true,
45157             /**
45158          * @event cellcontextmenu
45159          * Fires when a cell is right clicked
45160          * @param {Grid} this
45161          * @param {Number} rowIndex
45162          * @param {Number} cellIndex
45163          * @param {Roo.EventObject} e
45164          */
45165          "cellcontextmenu" : true,
45166             /**
45167              * @event headercontextmenu
45168              * Fires when a header is right clicked
45169              * @param {Grid} this
45170              * @param {Number} columnIndex
45171              * @param {Roo.EventObject} e
45172              */
45173             "headercontextmenu" : true,
45174             /**
45175              * @event bodyscroll
45176              * Fires when the body element is scrolled
45177              * @param {Number} scrollLeft
45178              * @param {Number} scrollTop
45179              */
45180             "bodyscroll" : true,
45181             /**
45182              * @event columnresize
45183              * Fires when the user resizes a column
45184              * @param {Number} columnIndex
45185              * @param {Number} newSize
45186              */
45187             "columnresize" : true,
45188             /**
45189              * @event columnmove
45190              * Fires when the user moves a column
45191              * @param {Number} oldIndex
45192              * @param {Number} newIndex
45193              */
45194             "columnmove" : true,
45195             /**
45196              * @event startdrag
45197              * Fires when row(s) start being dragged
45198              * @param {Grid} this
45199              * @param {Roo.GridDD} dd The drag drop object
45200              * @param {event} e The raw browser event
45201              */
45202             "startdrag" : true,
45203             /**
45204              * @event enddrag
45205              * Fires when a drag operation is complete
45206              * @param {Grid} this
45207              * @param {Roo.GridDD} dd The drag drop object
45208              * @param {event} e The raw browser event
45209              */
45210             "enddrag" : true,
45211             /**
45212              * @event dragdrop
45213              * Fires when dragged row(s) are dropped on a valid DD target
45214              * @param {Grid} this
45215              * @param {Roo.GridDD} dd The drag drop object
45216              * @param {String} targetId The target drag drop object
45217              * @param {event} e The raw browser event
45218              */
45219             "dragdrop" : true,
45220             /**
45221              * @event dragover
45222              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45223              * @param {Grid} this
45224              * @param {Roo.GridDD} dd The drag drop object
45225              * @param {String} targetId The target drag drop object
45226              * @param {event} e The raw browser event
45227              */
45228             "dragover" : true,
45229             /**
45230              * @event dragenter
45231              *  Fires when the dragged row(s) first cross another DD target while being dragged
45232              * @param {Grid} this
45233              * @param {Roo.GridDD} dd The drag drop object
45234              * @param {String} targetId The target drag drop object
45235              * @param {event} e The raw browser event
45236              */
45237             "dragenter" : true,
45238             /**
45239              * @event dragout
45240              * Fires when the dragged row(s) leave another DD target while being dragged
45241              * @param {Grid} this
45242              * @param {Roo.GridDD} dd The drag drop object
45243              * @param {String} targetId The target drag drop object
45244              * @param {event} e The raw browser event
45245              */
45246             "dragout" : true,
45247         /**
45248          * @event render
45249          * Fires when the grid is rendered
45250          * @param {Grid} grid
45251          */
45252         render : true
45253     });
45254
45255     Roo.grid.Grid.superclass.constructor.call(this);
45256 };
45257 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45258     /**
45259      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45260          */
45261         minColumnWidth : 25,
45262
45263     /**
45264          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45265          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45266          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45267          */
45268         autoSizeColumns : false,
45269
45270         /**
45271          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45272          */
45273         autoSizeHeaders : true,
45274
45275         /**
45276          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45277          */
45278         monitorWindowResize : true,
45279
45280         /**
45281          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45282          * rows measured to get a columns size. Default is 0 (all rows).
45283          */
45284         maxRowsToMeasure : 0,
45285
45286         /**
45287          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45288          */
45289         trackMouseOver : true,
45290
45291         /**
45292          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45293          */
45294         enableDragDrop : false,
45295
45296         /**
45297          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45298          */
45299         enableColumnMove : true,
45300
45301         /**
45302          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45303          */
45304         enableColumnHide : true,
45305
45306         /**
45307          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45308          */
45309         enableRowHeightSync : false,
45310
45311         /**
45312          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45313          */
45314         stripeRows : true,
45315
45316         /**
45317          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45318          */
45319         autoHeight : false,
45320
45321     /**
45322      * @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.
45323      */
45324     autoExpandColumn : false,
45325
45326     /**
45327     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45328     * Default is 50.
45329     */
45330     autoExpandMin : 50,
45331
45332     /**
45333     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45334     */
45335     autoExpandMax : 1000,
45336
45337     /**
45338          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45339          */
45340         view : null,
45341
45342         /**
45343      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45344          */
45345         loadMask : false,
45346
45347     // private
45348     rendered : false,
45349
45350     /**
45351     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45352     * of a fixed width. Default is false.
45353     */
45354     /**
45355     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45356     */
45357     /**
45358      * Called once after all setup has been completed and the grid is ready to be rendered.
45359      * @return {Roo.grid.Grid} this
45360      */
45361     render : function(){
45362         var c = this.container;
45363         // try to detect autoHeight/width mode
45364         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45365             this.autoHeight = true;
45366         }
45367         var view = this.getView();
45368         view.init(this);
45369
45370         c.on("click", this.onClick, this);
45371         c.on("dblclick", this.onDblClick, this);
45372         c.on("contextmenu", this.onContextMenu, this);
45373         c.on("keydown", this.onKeyDown, this);
45374
45375         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45376
45377         this.getSelectionModel().init(this);
45378
45379         view.render();
45380
45381         if(this.loadMask){
45382             this.loadMask = new Roo.LoadMask(this.container,
45383                     Roo.apply({store:this.dataSource}, this.loadMask));
45384         }
45385         
45386         
45387         if (this.toolbar && this.toolbar.xtype) {
45388             this.toolbar.container = this.getView().getHeaderPanel(true);
45389             this.toolbar = new Ext.Toolbar(this.toolbar);
45390         }
45391         if (this.footer && this.footer.xtype) {
45392             this.footer.dataSource = this.getDataSource();
45393             this.footer.container = this.getView().getFooterPanel(true);
45394             this.footer = Roo.factory(this.footer, Roo);
45395         }
45396         this.rendered = true;
45397         this.fireEvent('render', this);
45398         return this;
45399     },
45400
45401         /**
45402          * Reconfigures the grid to use a different Store and Column Model.
45403          * The View will be bound to the new objects and refreshed.
45404          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45405          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45406          */
45407     reconfigure : function(dataSource, colModel){
45408         if(this.loadMask){
45409             this.loadMask.destroy();
45410             this.loadMask = new Roo.LoadMask(this.container,
45411                     Roo.apply({store:dataSource}, this.loadMask));
45412         }
45413         this.view.bind(dataSource, colModel);
45414         this.dataSource = dataSource;
45415         this.colModel = colModel;
45416         this.view.refresh(true);
45417     },
45418
45419     // private
45420     onKeyDown : function(e){
45421         this.fireEvent("keydown", e);
45422     },
45423
45424     /**
45425      * Destroy this grid.
45426      * @param {Boolean} removeEl True to remove the element
45427      */
45428     destroy : function(removeEl, keepListeners){
45429         if(this.loadMask){
45430             this.loadMask.destroy();
45431         }
45432         var c = this.container;
45433         c.removeAllListeners();
45434         this.view.destroy();
45435         this.colModel.purgeListeners();
45436         if(!keepListeners){
45437             this.purgeListeners();
45438         }
45439         c.update("");
45440         if(removeEl === true){
45441             c.remove();
45442         }
45443     },
45444
45445     // private
45446     processEvent : function(name, e){
45447         this.fireEvent(name, e);
45448         var t = e.getTarget();
45449         var v = this.view;
45450         var header = v.findHeaderIndex(t);
45451         if(header !== false){
45452             this.fireEvent("header" + name, this, header, e);
45453         }else{
45454             var row = v.findRowIndex(t);
45455             var cell = v.findCellIndex(t);
45456             if(row !== false){
45457                 this.fireEvent("row" + name, this, row, e);
45458                 if(cell !== false){
45459                     this.fireEvent("cell" + name, this, row, cell, e);
45460                 }
45461             }
45462         }
45463     },
45464
45465     // private
45466     onClick : function(e){
45467         this.processEvent("click", e);
45468     },
45469
45470     // private
45471     onContextMenu : function(e, t){
45472         this.processEvent("contextmenu", e);
45473     },
45474
45475     // private
45476     onDblClick : function(e){
45477         this.processEvent("dblclick", e);
45478     },
45479
45480     // private
45481     walkCells : function(row, col, step, fn, scope){
45482         var cm = this.colModel, clen = cm.getColumnCount();
45483         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45484         if(step < 0){
45485             if(col < 0){
45486                 row--;
45487                 first = false;
45488             }
45489             while(row >= 0){
45490                 if(!first){
45491                     col = clen-1;
45492                 }
45493                 first = false;
45494                 while(col >= 0){
45495                     if(fn.call(scope || this, row, col, cm) === true){
45496                         return [row, col];
45497                     }
45498                     col--;
45499                 }
45500                 row--;
45501             }
45502         } else {
45503             if(col >= clen){
45504                 row++;
45505                 first = false;
45506             }
45507             while(row < rlen){
45508                 if(!first){
45509                     col = 0;
45510                 }
45511                 first = false;
45512                 while(col < clen){
45513                     if(fn.call(scope || this, row, col, cm) === true){
45514                         return [row, col];
45515                     }
45516                     col++;
45517                 }
45518                 row++;
45519             }
45520         }
45521         return null;
45522     },
45523
45524     // private
45525     getSelections : function(){
45526         return this.selModel.getSelections();
45527     },
45528
45529     /**
45530      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45531      * but if manual update is required this method will initiate it.
45532      */
45533     autoSize : function(){
45534         if(this.rendered){
45535             this.view.layout();
45536             if(this.view.adjustForScroll){
45537                 this.view.adjustForScroll();
45538             }
45539         }
45540     },
45541
45542     /**
45543      * Returns the grid's underlying element.
45544      * @return {Element} The element
45545      */
45546     getGridEl : function(){
45547         return this.container;
45548     },
45549
45550     // private for compatibility, overridden by editor grid
45551     stopEditing : function(){},
45552
45553     /**
45554      * Returns the grid's SelectionModel.
45555      * @return {SelectionModel}
45556      */
45557     getSelectionModel : function(){
45558         if(!this.selModel){
45559             this.selModel = new Roo.grid.RowSelectionModel();
45560         }
45561         return this.selModel;
45562     },
45563
45564     /**
45565      * Returns the grid's DataSource.
45566      * @return {DataSource}
45567      */
45568     getDataSource : function(){
45569         return this.dataSource;
45570     },
45571
45572     /**
45573      * Returns the grid's ColumnModel.
45574      * @return {ColumnModel}
45575      */
45576     getColumnModel : function(){
45577         return this.colModel;
45578     },
45579
45580     /**
45581      * Returns the grid's GridView object.
45582      * @return {GridView}
45583      */
45584     getView : function(){
45585         if(!this.view){
45586             this.view = new Roo.grid.GridView(this.viewConfig);
45587         }
45588         return this.view;
45589     },
45590     /**
45591      * Called to get grid's drag proxy text, by default returns this.ddText.
45592      * @return {String}
45593      */
45594     getDragDropText : function(){
45595         var count = this.selModel.getCount();
45596         return String.format(this.ddText, count, count == 1 ? '' : 's');
45597     }
45598 });
45599 /**
45600  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45601  * %0 is replaced with the number of selected rows.
45602  * @type String
45603  */
45604 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45605  * Based on:
45606  * Ext JS Library 1.1.1
45607  * Copyright(c) 2006-2007, Ext JS, LLC.
45608  *
45609  * Originally Released Under LGPL - original licence link has changed is not relivant.
45610  *
45611  * Fork - LGPL
45612  * <script type="text/javascript">
45613  */
45614  
45615 Roo.grid.AbstractGridView = function(){
45616         this.grid = null;
45617         
45618         this.events = {
45619             "beforerowremoved" : true,
45620             "beforerowsinserted" : true,
45621             "beforerefresh" : true,
45622             "rowremoved" : true,
45623             "rowsinserted" : true,
45624             "rowupdated" : true,
45625             "refresh" : true
45626         };
45627     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45628 };
45629
45630 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45631     rowClass : "x-grid-row",
45632     cellClass : "x-grid-cell",
45633     tdClass : "x-grid-td",
45634     hdClass : "x-grid-hd",
45635     splitClass : "x-grid-hd-split",
45636     
45637         init: function(grid){
45638         this.grid = grid;
45639                 var cid = this.grid.getGridEl().id;
45640         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45641         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45642         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45643         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45644         },
45645         
45646         getColumnRenderers : function(){
45647         var renderers = [];
45648         var cm = this.grid.colModel;
45649         var colCount = cm.getColumnCount();
45650         for(var i = 0; i < colCount; i++){
45651             renderers[i] = cm.getRenderer(i);
45652         }
45653         return renderers;
45654     },
45655     
45656     getColumnIds : function(){
45657         var ids = [];
45658         var cm = this.grid.colModel;
45659         var colCount = cm.getColumnCount();
45660         for(var i = 0; i < colCount; i++){
45661             ids[i] = cm.getColumnId(i);
45662         }
45663         return ids;
45664     },
45665     
45666     getDataIndexes : function(){
45667         if(!this.indexMap){
45668             this.indexMap = this.buildIndexMap();
45669         }
45670         return this.indexMap.colToData;
45671     },
45672     
45673     getColumnIndexByDataIndex : function(dataIndex){
45674         if(!this.indexMap){
45675             this.indexMap = this.buildIndexMap();
45676         }
45677         return this.indexMap.dataToCol[dataIndex];
45678     },
45679     
45680     /**
45681      * Set a css style for a column dynamically. 
45682      * @param {Number} colIndex The index of the column
45683      * @param {String} name The css property name
45684      * @param {String} value The css value
45685      */
45686     setCSSStyle : function(colIndex, name, value){
45687         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
45688         Roo.util.CSS.updateRule(selector, name, value);
45689     },
45690     
45691     generateRules : function(cm){
45692         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
45693         Roo.util.CSS.removeStyleSheet(rulesId);
45694         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
45695             var cid = cm.getColumnId(i);
45696             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
45697                          this.tdSelector, cid, " {\n}\n",
45698                          this.hdSelector, cid, " {\n}\n",
45699                          this.splitSelector, cid, " {\n}\n");
45700         }
45701         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
45702     }
45703 });/*
45704  * Based on:
45705  * Ext JS Library 1.1.1
45706  * Copyright(c) 2006-2007, Ext JS, LLC.
45707  *
45708  * Originally Released Under LGPL - original licence link has changed is not relivant.
45709  *
45710  * Fork - LGPL
45711  * <script type="text/javascript">
45712  */
45713
45714 // private
45715 // This is a support class used internally by the Grid components
45716 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
45717     this.grid = grid;
45718     this.view = grid.getView();
45719     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45720     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
45721     if(hd2){
45722         this.setHandleElId(Roo.id(hd));
45723         this.setOuterHandleElId(Roo.id(hd2));
45724     }
45725     this.scroll = false;
45726 };
45727 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
45728     maxDragWidth: 120,
45729     getDragData : function(e){
45730         var t = Roo.lib.Event.getTarget(e);
45731         var h = this.view.findHeaderCell(t);
45732         if(h){
45733             return {ddel: h.firstChild, header:h};
45734         }
45735         return false;
45736     },
45737
45738     onInitDrag : function(e){
45739         this.view.headersDisabled = true;
45740         var clone = this.dragData.ddel.cloneNode(true);
45741         clone.id = Roo.id();
45742         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
45743         this.proxy.update(clone);
45744         return true;
45745     },
45746
45747     afterValidDrop : function(){
45748         var v = this.view;
45749         setTimeout(function(){
45750             v.headersDisabled = false;
45751         }, 50);
45752     },
45753
45754     afterInvalidDrop : function(){
45755         var v = this.view;
45756         setTimeout(function(){
45757             v.headersDisabled = false;
45758         }, 50);
45759     }
45760 });
45761 /*
45762  * Based on:
45763  * Ext JS Library 1.1.1
45764  * Copyright(c) 2006-2007, Ext JS, LLC.
45765  *
45766  * Originally Released Under LGPL - original licence link has changed is not relivant.
45767  *
45768  * Fork - LGPL
45769  * <script type="text/javascript">
45770  */
45771 // private
45772 // This is a support class used internally by the Grid components
45773 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
45774     this.grid = grid;
45775     this.view = grid.getView();
45776     // split the proxies so they don't interfere with mouse events
45777     this.proxyTop = Roo.DomHelper.append(document.body, {
45778         cls:"col-move-top", html:"&#160;"
45779     }, true);
45780     this.proxyBottom = Roo.DomHelper.append(document.body, {
45781         cls:"col-move-bottom", html:"&#160;"
45782     }, true);
45783     this.proxyTop.hide = this.proxyBottom.hide = function(){
45784         this.setLeftTop(-100,-100);
45785         this.setStyle("visibility", "hidden");
45786     };
45787     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45788     // temporarily disabled
45789     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
45790     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
45791 };
45792 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
45793     proxyOffsets : [-4, -9],
45794     fly: Roo.Element.fly,
45795
45796     getTargetFromEvent : function(e){
45797         var t = Roo.lib.Event.getTarget(e);
45798         var cindex = this.view.findCellIndex(t);
45799         if(cindex !== false){
45800             return this.view.getHeaderCell(cindex);
45801         }
45802     },
45803
45804     nextVisible : function(h){
45805         var v = this.view, cm = this.grid.colModel;
45806         h = h.nextSibling;
45807         while(h){
45808             if(!cm.isHidden(v.getCellIndex(h))){
45809                 return h;
45810             }
45811             h = h.nextSibling;
45812         }
45813         return null;
45814     },
45815
45816     prevVisible : function(h){
45817         var v = this.view, cm = this.grid.colModel;
45818         h = h.prevSibling;
45819         while(h){
45820             if(!cm.isHidden(v.getCellIndex(h))){
45821                 return h;
45822             }
45823             h = h.prevSibling;
45824         }
45825         return null;
45826     },
45827
45828     positionIndicator : function(h, n, e){
45829         var x = Roo.lib.Event.getPageX(e);
45830         var r = Roo.lib.Dom.getRegion(n.firstChild);
45831         var px, pt, py = r.top + this.proxyOffsets[1];
45832         if((r.right - x) <= (r.right-r.left)/2){
45833             px = r.right+this.view.borderWidth;
45834             pt = "after";
45835         }else{
45836             px = r.left;
45837             pt = "before";
45838         }
45839         var oldIndex = this.view.getCellIndex(h);
45840         var newIndex = this.view.getCellIndex(n);
45841
45842         if(this.grid.colModel.isFixed(newIndex)){
45843             return false;
45844         }
45845
45846         var locked = this.grid.colModel.isLocked(newIndex);
45847
45848         if(pt == "after"){
45849             newIndex++;
45850         }
45851         if(oldIndex < newIndex){
45852             newIndex--;
45853         }
45854         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
45855             return false;
45856         }
45857         px +=  this.proxyOffsets[0];
45858         this.proxyTop.setLeftTop(px, py);
45859         this.proxyTop.show();
45860         if(!this.bottomOffset){
45861             this.bottomOffset = this.view.mainHd.getHeight();
45862         }
45863         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
45864         this.proxyBottom.show();
45865         return pt;
45866     },
45867
45868     onNodeEnter : function(n, dd, e, data){
45869         if(data.header != n){
45870             this.positionIndicator(data.header, n, e);
45871         }
45872     },
45873
45874     onNodeOver : function(n, dd, e, data){
45875         var result = false;
45876         if(data.header != n){
45877             result = this.positionIndicator(data.header, n, e);
45878         }
45879         if(!result){
45880             this.proxyTop.hide();
45881             this.proxyBottom.hide();
45882         }
45883         return result ? this.dropAllowed : this.dropNotAllowed;
45884     },
45885
45886     onNodeOut : function(n, dd, e, data){
45887         this.proxyTop.hide();
45888         this.proxyBottom.hide();
45889     },
45890
45891     onNodeDrop : function(n, dd, e, data){
45892         var h = data.header;
45893         if(h != n){
45894             var cm = this.grid.colModel;
45895             var x = Roo.lib.Event.getPageX(e);
45896             var r = Roo.lib.Dom.getRegion(n.firstChild);
45897             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
45898             var oldIndex = this.view.getCellIndex(h);
45899             var newIndex = this.view.getCellIndex(n);
45900             var locked = cm.isLocked(newIndex);
45901             if(pt == "after"){
45902                 newIndex++;
45903             }
45904             if(oldIndex < newIndex){
45905                 newIndex--;
45906             }
45907             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
45908                 return false;
45909             }
45910             cm.setLocked(oldIndex, locked, true);
45911             cm.moveColumn(oldIndex, newIndex);
45912             this.grid.fireEvent("columnmove", oldIndex, newIndex);
45913             return true;
45914         }
45915         return false;
45916     }
45917 });
45918 /*
45919  * Based on:
45920  * Ext JS Library 1.1.1
45921  * Copyright(c) 2006-2007, Ext JS, LLC.
45922  *
45923  * Originally Released Under LGPL - original licence link has changed is not relivant.
45924  *
45925  * Fork - LGPL
45926  * <script type="text/javascript">
45927  */
45928   
45929 /**
45930  * @class Roo.grid.GridView
45931  * @extends Roo.util.Observable
45932  *
45933  * @constructor
45934  * @param {Object} config
45935  */
45936 Roo.grid.GridView = function(config){
45937     Roo.grid.GridView.superclass.constructor.call(this);
45938     this.el = null;
45939
45940     Roo.apply(this, config);
45941 };
45942
45943 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
45944
45945     /**
45946      * Override this function to apply custom css classes to rows during rendering
45947      * @param {Record} record The record
45948      * @param {Number} index
45949      * @method getRowClass
45950      */
45951     rowClass : "x-grid-row",
45952
45953     cellClass : "x-grid-col",
45954
45955     tdClass : "x-grid-td",
45956
45957     hdClass : "x-grid-hd",
45958
45959     splitClass : "x-grid-split",
45960
45961     sortClasses : ["sort-asc", "sort-desc"],
45962
45963     enableMoveAnim : false,
45964
45965     hlColor: "C3DAF9",
45966
45967     dh : Roo.DomHelper,
45968
45969     fly : Roo.Element.fly,
45970
45971     css : Roo.util.CSS,
45972
45973     borderWidth: 1,
45974
45975     splitOffset: 3,
45976
45977     scrollIncrement : 22,
45978
45979     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
45980
45981     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
45982
45983     bind : function(ds, cm){
45984         if(this.ds){
45985             this.ds.un("load", this.onLoad, this);
45986             this.ds.un("datachanged", this.onDataChange, this);
45987             this.ds.un("add", this.onAdd, this);
45988             this.ds.un("remove", this.onRemove, this);
45989             this.ds.un("update", this.onUpdate, this);
45990             this.ds.un("clear", this.onClear, this);
45991         }
45992         if(ds){
45993             ds.on("load", this.onLoad, this);
45994             ds.on("datachanged", this.onDataChange, this);
45995             ds.on("add", this.onAdd, this);
45996             ds.on("remove", this.onRemove, this);
45997             ds.on("update", this.onUpdate, this);
45998             ds.on("clear", this.onClear, this);
45999         }
46000         this.ds = ds;
46001
46002         if(this.cm){
46003             this.cm.un("widthchange", this.onColWidthChange, this);
46004             this.cm.un("headerchange", this.onHeaderChange, this);
46005             this.cm.un("hiddenchange", this.onHiddenChange, this);
46006             this.cm.un("columnmoved", this.onColumnMove, this);
46007             this.cm.un("columnlockchange", this.onColumnLock, this);
46008         }
46009         if(cm){
46010             this.generateRules(cm);
46011             cm.on("widthchange", this.onColWidthChange, this);
46012             cm.on("headerchange", this.onHeaderChange, this);
46013             cm.on("hiddenchange", this.onHiddenChange, this);
46014             cm.on("columnmoved", this.onColumnMove, this);
46015             cm.on("columnlockchange", this.onColumnLock, this);
46016         }
46017         this.cm = cm;
46018     },
46019
46020     init: function(grid){
46021                 Roo.grid.GridView.superclass.init.call(this, grid);
46022
46023                 this.bind(grid.dataSource, grid.colModel);
46024
46025             grid.on("headerclick", this.handleHeaderClick, this);
46026
46027         if(grid.trackMouseOver){
46028             grid.on("mouseover", this.onRowOver, this);
46029                 grid.on("mouseout", this.onRowOut, this);
46030             }
46031             grid.cancelTextSelection = function(){};
46032                 this.gridId = grid.id;
46033
46034                 var tpls = this.templates || {};
46035
46036                 if(!tpls.master){
46037                     tpls.master = new Roo.Template(
46038                        '<div class="x-grid" hidefocus="true">',
46039                           '<div class="x-grid-topbar"></div>',
46040                           '<div class="x-grid-scroller"><div></div></div>',
46041                           '<div class="x-grid-locked">',
46042                               '<div class="x-grid-header">{lockedHeader}</div>',
46043                               '<div class="x-grid-body">{lockedBody}</div>',
46044                           "</div>",
46045                           '<div class="x-grid-viewport">',
46046                               '<div class="x-grid-header">{header}</div>',
46047                               '<div class="x-grid-body">{body}</div>',
46048                           "</div>",
46049                           '<div class="x-grid-bottombar"></div>',
46050                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46051                           '<div class="x-grid-resize-proxy">&#160;</div>',
46052                        "</div>"
46053                     );
46054                     tpls.master.disableformats = true;
46055                 }
46056
46057                 if(!tpls.header){
46058                     tpls.header = new Roo.Template(
46059                        '<table border="0" cellspacing="0" cellpadding="0">',
46060                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46061                        "</table>{splits}"
46062                     );
46063                     tpls.header.disableformats = true;
46064                 }
46065                 tpls.header.compile();
46066
46067                 if(!tpls.hcell){
46068                     tpls.hcell = new Roo.Template(
46069                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46070                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46071                         "</div></td>"
46072                      );
46073                      tpls.hcell.disableFormats = true;
46074                 }
46075                 tpls.hcell.compile();
46076
46077                 if(!tpls.hsplit){
46078                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46079                     tpls.hsplit.disableFormats = true;
46080                 }
46081                 tpls.hsplit.compile();
46082
46083                 if(!tpls.body){
46084                     tpls.body = new Roo.Template(
46085                        '<table border="0" cellspacing="0" cellpadding="0">',
46086                        "<tbody>{rows}</tbody>",
46087                        "</table>"
46088                     );
46089                     tpls.body.disableFormats = true;
46090                 }
46091                 tpls.body.compile();
46092
46093                 if(!tpls.row){
46094                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46095                     tpls.row.disableFormats = true;
46096                 }
46097                 tpls.row.compile();
46098
46099                 if(!tpls.cell){
46100                     tpls.cell = new Roo.Template(
46101                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46102                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46103                         "</td>"
46104                     );
46105             tpls.cell.disableFormats = true;
46106         }
46107                 tpls.cell.compile();
46108
46109                 this.templates = tpls;
46110         },
46111
46112         // remap these for backwards compat
46113     onColWidthChange : function(){
46114         this.updateColumns.apply(this, arguments);
46115     },
46116     onHeaderChange : function(){
46117         this.updateHeaders.apply(this, arguments);
46118     }, 
46119     onHiddenChange : function(){
46120         this.handleHiddenChange.apply(this, arguments);
46121     },
46122     onColumnMove : function(){
46123         this.handleColumnMove.apply(this, arguments);
46124     },
46125     onColumnLock : function(){
46126         this.handleLockChange.apply(this, arguments);
46127     },
46128
46129     onDataChange : function(){
46130         this.refresh();
46131         this.updateHeaderSortState();
46132     },
46133
46134         onClear : function(){
46135         this.refresh();
46136     },
46137
46138         onUpdate : function(ds, record){
46139         this.refreshRow(record);
46140     },
46141
46142     refreshRow : function(record){
46143         var ds = this.ds, index;
46144         if(typeof record == 'number'){
46145             index = record;
46146             record = ds.getAt(index);
46147         }else{
46148             index = ds.indexOf(record);
46149         }
46150         this.insertRows(ds, index, index, true);
46151         this.onRemove(ds, record, index+1, true);
46152         this.syncRowHeights(index, index);
46153         this.layout();
46154         this.fireEvent("rowupdated", this, index, record);
46155     },
46156
46157     onAdd : function(ds, records, index){
46158         this.insertRows(ds, index, index + (records.length-1));
46159     },
46160
46161     onRemove : function(ds, record, index, isUpdate){
46162         if(isUpdate !== true){
46163             this.fireEvent("beforerowremoved", this, index, record);
46164         }
46165         var bt = this.getBodyTable(), lt = this.getLockedTable();
46166         if(bt.rows[index]){
46167             bt.firstChild.removeChild(bt.rows[index]);
46168         }
46169         if(lt.rows[index]){
46170             lt.firstChild.removeChild(lt.rows[index]);
46171         }
46172         if(isUpdate !== true){
46173             this.stripeRows(index);
46174             this.syncRowHeights(index, index);
46175             this.layout();
46176             this.fireEvent("rowremoved", this, index, record);
46177         }
46178     },
46179
46180     onLoad : function(){
46181         this.scrollToTop();
46182     },
46183
46184     /**
46185      * Scrolls the grid to the top
46186      */
46187     scrollToTop : function(){
46188         if(this.scroller){
46189             this.scroller.dom.scrollTop = 0;
46190             this.syncScroll();
46191         }
46192     },
46193
46194     /**
46195      * Gets a panel in the header of the grid that can be used for toolbars etc.
46196      * After modifying the contents of this panel a call to grid.autoSize() may be
46197      * required to register any changes in size.
46198      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46199      * @return Roo.Element
46200      */
46201     getHeaderPanel : function(doShow){
46202         if(doShow){
46203             this.headerPanel.show();
46204         }
46205         return this.headerPanel;
46206         },
46207
46208         /**
46209      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46210      * After modifying the contents of this panel a call to grid.autoSize() may be
46211      * required to register any changes in size.
46212      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46213      * @return Roo.Element
46214      */
46215     getFooterPanel : function(doShow){
46216         if(doShow){
46217             this.footerPanel.show();
46218         }
46219         return this.footerPanel;
46220         },
46221
46222         initElements : function(){
46223             var E = Roo.Element;
46224             var el = this.grid.getGridEl().dom.firstChild;
46225             var cs = el.childNodes;
46226
46227             this.el = new E(el);
46228             this.headerPanel = new E(el.firstChild);
46229             this.headerPanel.enableDisplayMode("block");
46230
46231         this.scroller = new E(cs[1]);
46232             this.scrollSizer = new E(this.scroller.dom.firstChild);
46233
46234             this.lockedWrap = new E(cs[2]);
46235             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46236             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46237
46238             this.mainWrap = new E(cs[3]);
46239             this.mainHd = new E(this.mainWrap.dom.firstChild);
46240             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46241
46242             this.footerPanel = new E(cs[4]);
46243             this.footerPanel.enableDisplayMode("block");
46244
46245         this.focusEl = new E(cs[5]);
46246         this.focusEl.swallowEvent("click", true);
46247         this.resizeProxy = new E(cs[6]);
46248
46249             this.headerSelector = String.format(
46250                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46251                this.lockedHd.id, this.mainHd.id
46252             );
46253
46254             this.splitterSelector = String.format(
46255                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46256                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46257             );
46258     },
46259     idToCssName : function(s)
46260     {
46261         return s.replace(/[^a-z0-9]+/ig, '-');
46262     },
46263
46264         getHeaderCell : function(index){
46265             return Roo.DomQuery.select(this.headerSelector)[index];
46266         },
46267
46268         getHeaderCellMeasure : function(index){
46269             return this.getHeaderCell(index).firstChild;
46270         },
46271
46272         getHeaderCellText : function(index){
46273             return this.getHeaderCell(index).firstChild.firstChild;
46274         },
46275
46276         getLockedTable : function(){
46277             return this.lockedBody.dom.firstChild;
46278         },
46279
46280         getBodyTable : function(){
46281             return this.mainBody.dom.firstChild;
46282         },
46283
46284         getLockedRow : function(index){
46285             return this.getLockedTable().rows[index];
46286         },
46287
46288         getRow : function(index){
46289             return this.getBodyTable().rows[index];
46290         },
46291
46292         getRowComposite : function(index){
46293             if(!this.rowEl){
46294                 this.rowEl = new Roo.CompositeElementLite();
46295             }
46296         var els = [], lrow, mrow;
46297         if(lrow = this.getLockedRow(index)){
46298             els.push(lrow);
46299         }
46300         if(mrow = this.getRow(index)){
46301             els.push(mrow);
46302         }
46303         this.rowEl.elements = els;
46304             return this.rowEl;
46305         },
46306
46307         getCell : function(rowIndex, colIndex){
46308             var locked = this.cm.getLockedCount();
46309             var source;
46310             if(colIndex < locked){
46311                 source = this.lockedBody.dom.firstChild;
46312             }else{
46313                 source = this.mainBody.dom.firstChild;
46314                 colIndex -= locked;
46315             }
46316         return source.rows[rowIndex].childNodes[colIndex];
46317         },
46318
46319         getCellText : function(rowIndex, colIndex){
46320             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46321         },
46322
46323         getCellBox : function(cell){
46324             var b = this.fly(cell).getBox();
46325         if(Roo.isOpera){ // opera fails to report the Y
46326             b.y = cell.offsetTop + this.mainBody.getY();
46327         }
46328         return b;
46329     },
46330
46331     getCellIndex : function(cell){
46332         var id = String(cell.className).match(this.cellRE);
46333         if(id){
46334             return parseInt(id[1], 10);
46335         }
46336         return 0;
46337     },
46338
46339     findHeaderIndex : function(n){
46340         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46341         return r ? this.getCellIndex(r) : false;
46342     },
46343
46344     findHeaderCell : function(n){
46345         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46346         return r ? r : false;
46347     },
46348
46349     findRowIndex : function(n){
46350         if(!n){
46351             return false;
46352         }
46353         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46354         return r ? r.rowIndex : false;
46355     },
46356
46357     findCellIndex : function(node){
46358         var stop = this.el.dom;
46359         while(node && node != stop){
46360             if(this.findRE.test(node.className)){
46361                 return this.getCellIndex(node);
46362             }
46363             node = node.parentNode;
46364         }
46365         return false;
46366     },
46367
46368     getColumnId : function(index){
46369             return this.cm.getColumnId(index);
46370         },
46371
46372         getSplitters : function(){
46373             if(this.splitterSelector){
46374                return Roo.DomQuery.select(this.splitterSelector);
46375             }else{
46376                 return null;
46377             }
46378         },
46379
46380         getSplitter : function(index){
46381             return this.getSplitters()[index];
46382         },
46383
46384     onRowOver : function(e, t){
46385         var row;
46386         if((row = this.findRowIndex(t)) !== false){
46387             this.getRowComposite(row).addClass("x-grid-row-over");
46388         }
46389     },
46390
46391     onRowOut : function(e, t){
46392         var row;
46393         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46394             this.getRowComposite(row).removeClass("x-grid-row-over");
46395         }
46396     },
46397
46398     renderHeaders : function(){
46399             var cm = this.cm;
46400         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46401         var cb = [], lb = [], sb = [], lsb = [], p = {};
46402         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46403             p.cellId = "x-grid-hd-0-" + i;
46404             p.splitId = "x-grid-csplit-0-" + i;
46405             p.id = cm.getColumnId(i);
46406             p.title = cm.getColumnTooltip(i) || "";
46407             p.value = cm.getColumnHeader(i) || "";
46408             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46409             if(!cm.isLocked(i)){
46410                 cb[cb.length] = ct.apply(p);
46411                 sb[sb.length] = st.apply(p);
46412             }else{
46413                 lb[lb.length] = ct.apply(p);
46414                 lsb[lsb.length] = st.apply(p);
46415             }
46416         }
46417         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46418                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46419         },
46420
46421         updateHeaders : function(){
46422         var html = this.renderHeaders();
46423         this.lockedHd.update(html[0]);
46424         this.mainHd.update(html[1]);
46425     },
46426
46427     /**
46428      * Focuses the specified row.
46429      * @param {Number} row The row index
46430      */
46431     focusRow : function(row){
46432         var x = this.scroller.dom.scrollLeft;
46433         this.focusCell(row, 0, false);
46434         this.scroller.dom.scrollLeft = x;
46435     },
46436
46437     /**
46438      * Focuses the specified cell.
46439      * @param {Number} row The row index
46440      * @param {Number} col The column index
46441      * @param {Boolean} hscroll false to disable horizontal scrolling
46442      */
46443     focusCell : function(row, col, hscroll){
46444         var el = this.ensureVisible(row, col, hscroll);
46445         this.focusEl.alignTo(el, "tl-tl");
46446         if(Roo.isGecko){
46447             this.focusEl.focus();
46448         }else{
46449             this.focusEl.focus.defer(1, this.focusEl);
46450         }
46451     },
46452
46453     /**
46454      * Scrolls the specified cell into view
46455      * @param {Number} row The row index
46456      * @param {Number} col The column index
46457      * @param {Boolean} hscroll false to disable horizontal scrolling
46458      */
46459     ensureVisible : function(row, col, hscroll){
46460         if(typeof row != "number"){
46461             row = row.rowIndex;
46462         }
46463         if(row < 0 && row >= this.ds.getCount()){
46464             return;
46465         }
46466         col = (col !== undefined ? col : 0);
46467         var cm = this.grid.colModel;
46468         while(cm.isHidden(col)){
46469             col++;
46470         }
46471
46472         var el = this.getCell(row, col);
46473         if(!el){
46474             return;
46475         }
46476         var c = this.scroller.dom;
46477
46478         var ctop = parseInt(el.offsetTop, 10);
46479         var cleft = parseInt(el.offsetLeft, 10);
46480         var cbot = ctop + el.offsetHeight;
46481         var cright = cleft + el.offsetWidth;
46482
46483         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46484         var stop = parseInt(c.scrollTop, 10);
46485         var sleft = parseInt(c.scrollLeft, 10);
46486         var sbot = stop + ch;
46487         var sright = sleft + c.clientWidth;
46488
46489         if(ctop < stop){
46490                 c.scrollTop = ctop;
46491         }else if(cbot > sbot){
46492             c.scrollTop = cbot-ch;
46493         }
46494
46495         if(hscroll !== false){
46496             if(cleft < sleft){
46497                 c.scrollLeft = cleft;
46498             }else if(cright > sright){
46499                 c.scrollLeft = cright-c.clientWidth;
46500             }
46501         }
46502         return el;
46503     },
46504
46505     updateColumns : function(){
46506         this.grid.stopEditing();
46507         var cm = this.grid.colModel, colIds = this.getColumnIds();
46508         //var totalWidth = cm.getTotalWidth();
46509         var pos = 0;
46510         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46511             //if(cm.isHidden(i)) continue;
46512             var w = cm.getColumnWidth(i);
46513             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46514             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46515         }
46516         this.updateSplitters();
46517     },
46518
46519     generateRules : function(cm){
46520         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46521         Roo.util.CSS.removeStyleSheet(rulesId);
46522         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46523             var cid = cm.getColumnId(i);
46524             var align = '';
46525             if(cm.config[i].align){
46526                 align = 'text-align:'+cm.config[i].align+';';
46527             }
46528             var hidden = '';
46529             if(cm.isHidden(i)){
46530                 hidden = 'display:none;';
46531             }
46532             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46533             ruleBuf.push(
46534                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46535                     this.hdSelector, cid, " {\n", align, width, "}\n",
46536                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46537                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46538         }
46539         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46540     },
46541
46542     updateSplitters : function(){
46543         var cm = this.cm, s = this.getSplitters();
46544         if(s){ // splitters not created yet
46545             var pos = 0, locked = true;
46546             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46547                 if(cm.isHidden(i)) continue;
46548                 var w = cm.getColumnWidth(i);
46549                 if(!cm.isLocked(i) && locked){
46550                     pos = 0;
46551                     locked = false;
46552                 }
46553                 pos += w;
46554                 s[i].style.left = (pos-this.splitOffset) + "px";
46555             }
46556         }
46557     },
46558
46559     handleHiddenChange : function(colModel, colIndex, hidden){
46560         if(hidden){
46561             this.hideColumn(colIndex);
46562         }else{
46563             this.unhideColumn(colIndex);
46564         }
46565     },
46566
46567     hideColumn : function(colIndex){
46568         var cid = this.getColumnId(colIndex);
46569         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46570         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46571         if(Roo.isSafari){
46572             this.updateHeaders();
46573         }
46574         this.updateSplitters();
46575         this.layout();
46576     },
46577
46578     unhideColumn : function(colIndex){
46579         var cid = this.getColumnId(colIndex);
46580         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46581         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46582
46583         if(Roo.isSafari){
46584             this.updateHeaders();
46585         }
46586         this.updateSplitters();
46587         this.layout();
46588     },
46589
46590     insertRows : function(dm, firstRow, lastRow, isUpdate){
46591         if(firstRow == 0 && lastRow == dm.getCount()-1){
46592             this.refresh();
46593         }else{
46594             if(!isUpdate){
46595                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46596             }
46597             var s = this.getScrollState();
46598             var markup = this.renderRows(firstRow, lastRow);
46599             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46600             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46601             this.restoreScroll(s);
46602             if(!isUpdate){
46603                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46604                 this.syncRowHeights(firstRow, lastRow);
46605                 this.stripeRows(firstRow);
46606                 this.layout();
46607             }
46608         }
46609     },
46610
46611     bufferRows : function(markup, target, index){
46612         var before = null, trows = target.rows, tbody = target.tBodies[0];
46613         if(index < trows.length){
46614             before = trows[index];
46615         }
46616         var b = document.createElement("div");
46617         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46618         var rows = b.firstChild.rows;
46619         for(var i = 0, len = rows.length; i < len; i++){
46620             if(before){
46621                 tbody.insertBefore(rows[0], before);
46622             }else{
46623                 tbody.appendChild(rows[0]);
46624             }
46625         }
46626         b.innerHTML = "";
46627         b = null;
46628     },
46629
46630     deleteRows : function(dm, firstRow, lastRow){
46631         if(dm.getRowCount()<1){
46632             this.fireEvent("beforerefresh", this);
46633             this.mainBody.update("");
46634             this.lockedBody.update("");
46635             this.fireEvent("refresh", this);
46636         }else{
46637             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46638             var bt = this.getBodyTable();
46639             var tbody = bt.firstChild;
46640             var rows = bt.rows;
46641             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46642                 tbody.removeChild(rows[firstRow]);
46643             }
46644             this.stripeRows(firstRow);
46645             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46646         }
46647     },
46648
46649     updateRows : function(dataSource, firstRow, lastRow){
46650         var s = this.getScrollState();
46651         this.refresh();
46652         this.restoreScroll(s);
46653     },
46654
46655     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46656         if(!noRefresh){
46657            this.refresh();
46658         }
46659         this.updateHeaderSortState();
46660     },
46661
46662     getScrollState : function(){
46663         var sb = this.scroller.dom;
46664         return {left: sb.scrollLeft, top: sb.scrollTop};
46665     },
46666
46667     stripeRows : function(startRow){
46668         if(!this.grid.stripeRows || this.ds.getCount() < 1){
46669             return;
46670         }
46671         startRow = startRow || 0;
46672         var rows = this.getBodyTable().rows;
46673         var lrows = this.getLockedTable().rows;
46674         var cls = ' x-grid-row-alt ';
46675         for(var i = startRow, len = rows.length; i < len; i++){
46676             var row = rows[i], lrow = lrows[i];
46677             var isAlt = ((i+1) % 2 == 0);
46678             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
46679             if(isAlt == hasAlt){
46680                 continue;
46681             }
46682             if(isAlt){
46683                 row.className += " x-grid-row-alt";
46684             }else{
46685                 row.className = row.className.replace("x-grid-row-alt", "");
46686             }
46687             if(lrow){
46688                 lrow.className = row.className;
46689             }
46690         }
46691     },
46692
46693     restoreScroll : function(state){
46694         var sb = this.scroller.dom;
46695         sb.scrollLeft = state.left;
46696         sb.scrollTop = state.top;
46697         this.syncScroll();
46698     },
46699
46700     syncScroll : function(){
46701         var sb = this.scroller.dom;
46702         var sh = this.mainHd.dom;
46703         var bs = this.mainBody.dom;
46704         var lv = this.lockedBody.dom;
46705         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
46706         lv.scrollTop = bs.scrollTop = sb.scrollTop;
46707     },
46708
46709     handleScroll : function(e){
46710         this.syncScroll();
46711         var sb = this.scroller.dom;
46712         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
46713         e.stopEvent();
46714     },
46715
46716     handleWheel : function(e){
46717         var d = e.getWheelDelta();
46718         this.scroller.dom.scrollTop -= d*22;
46719         // set this here to prevent jumpy scrolling on large tables
46720         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
46721         e.stopEvent();
46722     },
46723
46724     renderRows : function(startRow, endRow){
46725         // pull in all the crap needed to render rows
46726         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
46727         var colCount = cm.getColumnCount();
46728
46729         if(ds.getCount() < 1){
46730             return ["", ""];
46731         }
46732
46733         // build a map for all the columns
46734         var cs = [];
46735         for(var i = 0; i < colCount; i++){
46736             var name = cm.getDataIndex(i);
46737             cs[i] = {
46738                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
46739                 renderer : cm.getRenderer(i),
46740                 id : cm.getColumnId(i),
46741                 locked : cm.isLocked(i)
46742             };
46743         }
46744
46745         startRow = startRow || 0;
46746         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
46747
46748         // records to render
46749         var rs = ds.getRange(startRow, endRow);
46750
46751         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
46752     },
46753
46754     // As much as I hate to duplicate code, this was branched because FireFox really hates
46755     // [].join("") on strings. The performance difference was substantial enough to
46756     // branch this function
46757     doRender : Roo.isGecko ?
46758             function(cs, rs, ds, startRow, colCount, stripe){
46759                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46760                 // buffers
46761                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46762                 for(var j = 0, len = rs.length; j < len; j++){
46763                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
46764                     for(var i = 0; i < colCount; i++){
46765                         c = cs[i];
46766                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46767                         p.id = c.id;
46768                         p.css = p.attr = "";
46769                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46770                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46771                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46772                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46773                         }
46774                         var markup = ct.apply(p);
46775                         if(!c.locked){
46776                             cb+= markup;
46777                         }else{
46778                             lcb+= markup;
46779                         }
46780                     }
46781                     var alt = [];
46782                     if(stripe && ((rowIndex+1) % 2 == 0)){
46783                         alt[0] = "x-grid-row-alt";
46784                     }
46785                     if(r.dirty){
46786                         alt[1] = " x-grid-dirty-row";
46787                     }
46788                     rp.cells = lcb;
46789                     if(this.getRowClass){
46790                         alt[2] = this.getRowClass(r, rowIndex);
46791                     }
46792                     rp.alt = alt.join(" ");
46793                     lbuf+= rt.apply(rp);
46794                     rp.cells = cb;
46795                     buf+=  rt.apply(rp);
46796                 }
46797                 return [lbuf, buf];
46798             } :
46799             function(cs, rs, ds, startRow, colCount, stripe){
46800                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46801                 // buffers
46802                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46803                 for(var j = 0, len = rs.length; j < len; j++){
46804                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
46805                     for(var i = 0; i < colCount; i++){
46806                         c = cs[i];
46807                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46808                         p.id = c.id;
46809                         p.css = p.attr = "";
46810                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46811                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46812                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46813                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46814                         }
46815                         var markup = ct.apply(p);
46816                         if(!c.locked){
46817                             cb[cb.length] = markup;
46818                         }else{
46819                             lcb[lcb.length] = markup;
46820                         }
46821                     }
46822                     var alt = [];
46823                     if(stripe && ((rowIndex+1) % 2 == 0)){
46824                         alt[0] = "x-grid-row-alt";
46825                     }
46826                     if(r.dirty){
46827                         alt[1] = " x-grid-dirty-row";
46828                     }
46829                     rp.cells = lcb;
46830                     if(this.getRowClass){
46831                         alt[2] = this.getRowClass(r, rowIndex);
46832                     }
46833                     rp.alt = alt.join(" ");
46834                     rp.cells = lcb.join("");
46835                     lbuf[lbuf.length] = rt.apply(rp);
46836                     rp.cells = cb.join("");
46837                     buf[buf.length] =  rt.apply(rp);
46838                 }
46839                 return [lbuf.join(""), buf.join("")];
46840             },
46841
46842     renderBody : function(){
46843         var markup = this.renderRows();
46844         var bt = this.templates.body;
46845         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
46846     },
46847
46848     /**
46849      * Refreshes the grid
46850      * @param {Boolean} headersToo
46851      */
46852     refresh : function(headersToo){
46853         this.fireEvent("beforerefresh", this);
46854         this.grid.stopEditing();
46855         var result = this.renderBody();
46856         this.lockedBody.update(result[0]);
46857         this.mainBody.update(result[1]);
46858         if(headersToo === true){
46859             this.updateHeaders();
46860             this.updateColumns();
46861             this.updateSplitters();
46862             this.updateHeaderSortState();
46863         }
46864         this.syncRowHeights();
46865         this.layout();
46866         this.fireEvent("refresh", this);
46867     },
46868
46869     handleColumnMove : function(cm, oldIndex, newIndex){
46870         this.indexMap = null;
46871         var s = this.getScrollState();
46872         this.refresh(true);
46873         this.restoreScroll(s);
46874         this.afterMove(newIndex);
46875     },
46876
46877     afterMove : function(colIndex){
46878         if(this.enableMoveAnim && Roo.enableFx){
46879             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
46880         }
46881     },
46882
46883     updateCell : function(dm, rowIndex, dataIndex){
46884         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
46885         if(typeof colIndex == "undefined"){ // not present in grid
46886             return;
46887         }
46888         var cm = this.grid.colModel;
46889         var cell = this.getCell(rowIndex, colIndex);
46890         var cellText = this.getCellText(rowIndex, colIndex);
46891
46892         var p = {
46893             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
46894             id : cm.getColumnId(colIndex),
46895             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
46896         };
46897         var renderer = cm.getRenderer(colIndex);
46898         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
46899         if(typeof val == "undefined" || val === "") val = "&#160;";
46900         cellText.innerHTML = val;
46901         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
46902         this.syncRowHeights(rowIndex, rowIndex);
46903     },
46904
46905     calcColumnWidth : function(colIndex, maxRowsToMeasure){
46906         var maxWidth = 0;
46907         if(this.grid.autoSizeHeaders){
46908             var h = this.getHeaderCellMeasure(colIndex);
46909             maxWidth = Math.max(maxWidth, h.scrollWidth);
46910         }
46911         var tb, index;
46912         if(this.cm.isLocked(colIndex)){
46913             tb = this.getLockedTable();
46914             index = colIndex;
46915         }else{
46916             tb = this.getBodyTable();
46917             index = colIndex - this.cm.getLockedCount();
46918         }
46919         if(tb && tb.rows){
46920             var rows = tb.rows;
46921             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
46922             for(var i = 0; i < stopIndex; i++){
46923                 var cell = rows[i].childNodes[index].firstChild;
46924                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
46925             }
46926         }
46927         return maxWidth + /*margin for error in IE*/ 5;
46928     },
46929     /**
46930      * Autofit a column to its content.
46931      * @param {Number} colIndex
46932      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
46933      */
46934      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
46935          if(this.cm.isHidden(colIndex)){
46936              return; // can't calc a hidden column
46937          }
46938         if(forceMinSize){
46939             var cid = this.cm.getColumnId(colIndex);
46940             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
46941            if(this.grid.autoSizeHeaders){
46942                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
46943            }
46944         }
46945         var newWidth = this.calcColumnWidth(colIndex);
46946         this.cm.setColumnWidth(colIndex,
46947             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
46948         if(!suppressEvent){
46949             this.grid.fireEvent("columnresize", colIndex, newWidth);
46950         }
46951     },
46952
46953     /**
46954      * Autofits all columns to their content and then expands to fit any extra space in the grid
46955      */
46956      autoSizeColumns : function(){
46957         var cm = this.grid.colModel;
46958         var colCount = cm.getColumnCount();
46959         for(var i = 0; i < colCount; i++){
46960             this.autoSizeColumn(i, true, true);
46961         }
46962         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
46963             this.fitColumns();
46964         }else{
46965             this.updateColumns();
46966             this.layout();
46967         }
46968     },
46969
46970     /**
46971      * Autofits all columns to the grid's width proportionate with their current size
46972      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
46973      */
46974     fitColumns : function(reserveScrollSpace){
46975         var cm = this.grid.colModel;
46976         var colCount = cm.getColumnCount();
46977         var cols = [];
46978         var width = 0;
46979         var i, w;
46980         for (i = 0; i < colCount; i++){
46981             if(!cm.isHidden(i) && !cm.isFixed(i)){
46982                 w = cm.getColumnWidth(i);
46983                 cols.push(i);
46984                 cols.push(w);
46985                 width += w;
46986             }
46987         }
46988         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
46989         if(reserveScrollSpace){
46990             avail -= 17;
46991         }
46992         var frac = (avail - cm.getTotalWidth())/width;
46993         while (cols.length){
46994             w = cols.pop();
46995             i = cols.pop();
46996             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
46997         }
46998         this.updateColumns();
46999         this.layout();
47000     },
47001
47002     onRowSelect : function(rowIndex){
47003         var row = this.getRowComposite(rowIndex);
47004         row.addClass("x-grid-row-selected");
47005     },
47006
47007     onRowDeselect : function(rowIndex){
47008         var row = this.getRowComposite(rowIndex);
47009         row.removeClass("x-grid-row-selected");
47010     },
47011
47012     onCellSelect : function(row, col){
47013         var cell = this.getCell(row, col);
47014         if(cell){
47015             Roo.fly(cell).addClass("x-grid-cell-selected");
47016         }
47017     },
47018
47019     onCellDeselect : function(row, col){
47020         var cell = this.getCell(row, col);
47021         if(cell){
47022             Roo.fly(cell).removeClass("x-grid-cell-selected");
47023         }
47024     },
47025
47026     updateHeaderSortState : function(){
47027         var state = this.ds.getSortState();
47028         if(!state){
47029             return;
47030         }
47031         this.sortState = state;
47032         var sortColumn = this.cm.findColumnIndex(state.field);
47033         if(sortColumn != -1){
47034             var sortDir = state.direction;
47035             var sc = this.sortClasses;
47036             var hds = this.el.select(this.headerSelector).removeClass(sc);
47037             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47038         }
47039     },
47040
47041     handleHeaderClick : function(g, index){
47042         if(this.headersDisabled){
47043             return;
47044         }
47045         var dm = g.dataSource, cm = g.colModel;
47046             if(!cm.isSortable(index)){
47047             return;
47048         }
47049             g.stopEditing();
47050         dm.sort(cm.getDataIndex(index));
47051     },
47052
47053
47054     destroy : function(){
47055         if(this.colMenu){
47056             this.colMenu.removeAll();
47057             Roo.menu.MenuMgr.unregister(this.colMenu);
47058             this.colMenu.getEl().remove();
47059             delete this.colMenu;
47060         }
47061         if(this.hmenu){
47062             this.hmenu.removeAll();
47063             Roo.menu.MenuMgr.unregister(this.hmenu);
47064             this.hmenu.getEl().remove();
47065             delete this.hmenu;
47066         }
47067         if(this.grid.enableColumnMove){
47068             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47069             if(dds){
47070                 for(var dd in dds){
47071                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47072                         var elid = dds[dd].dragElId;
47073                         dds[dd].unreg();
47074                         Roo.get(elid).remove();
47075                     } else if(dds[dd].config.isTarget){
47076                         dds[dd].proxyTop.remove();
47077                         dds[dd].proxyBottom.remove();
47078                         dds[dd].unreg();
47079                     }
47080                     if(Roo.dd.DDM.locationCache[dd]){
47081                         delete Roo.dd.DDM.locationCache[dd];
47082                     }
47083                 }
47084                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47085             }
47086         }
47087         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47088         this.bind(null, null);
47089         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47090     },
47091
47092     handleLockChange : function(){
47093         this.refresh(true);
47094     },
47095
47096     onDenyColumnLock : function(){
47097
47098     },
47099
47100     onDenyColumnHide : function(){
47101
47102     },
47103
47104     handleHdMenuClick : function(item){
47105         var index = this.hdCtxIndex;
47106         var cm = this.cm, ds = this.ds;
47107         switch(item.id){
47108             case "asc":
47109                 ds.sort(cm.getDataIndex(index), "ASC");
47110                 break;
47111             case "desc":
47112                 ds.sort(cm.getDataIndex(index), "DESC");
47113                 break;
47114             case "lock":
47115                 var lc = cm.getLockedCount();
47116                 if(cm.getColumnCount(true) <= lc+1){
47117                     this.onDenyColumnLock();
47118                     return;
47119                 }
47120                 if(lc != index){
47121                     cm.setLocked(index, true, true);
47122                     cm.moveColumn(index, lc);
47123                     this.grid.fireEvent("columnmove", index, lc);
47124                 }else{
47125                     cm.setLocked(index, true);
47126                 }
47127             break;
47128             case "unlock":
47129                 var lc = cm.getLockedCount();
47130                 if((lc-1) != index){
47131                     cm.setLocked(index, false, true);
47132                     cm.moveColumn(index, lc-1);
47133                     this.grid.fireEvent("columnmove", index, lc-1);
47134                 }else{
47135                     cm.setLocked(index, false);
47136                 }
47137             break;
47138             default:
47139                 index = cm.getIndexById(item.id.substr(4));
47140                 if(index != -1){
47141                     if(item.checked && cm.getColumnCount(true) <= 1){
47142                         this.onDenyColumnHide();
47143                         return false;
47144                     }
47145                     cm.setHidden(index, item.checked);
47146                 }
47147         }
47148         return true;
47149     },
47150
47151     beforeColMenuShow : function(){
47152         var cm = this.cm,  colCount = cm.getColumnCount();
47153         this.colMenu.removeAll();
47154         for(var i = 0; i < colCount; i++){
47155             this.colMenu.add(new Roo.menu.CheckItem({
47156                 id: "col-"+cm.getColumnId(i),
47157                 text: cm.getColumnHeader(i),
47158                 checked: !cm.isHidden(i),
47159                 hideOnClick:false
47160             }));
47161         }
47162     },
47163
47164     handleHdCtx : function(g, index, e){
47165         e.stopEvent();
47166         var hd = this.getHeaderCell(index);
47167         this.hdCtxIndex = index;
47168         var ms = this.hmenu.items, cm = this.cm;
47169         ms.get("asc").setDisabled(!cm.isSortable(index));
47170         ms.get("desc").setDisabled(!cm.isSortable(index));
47171         if(this.grid.enableColLock !== false){
47172             ms.get("lock").setDisabled(cm.isLocked(index));
47173             ms.get("unlock").setDisabled(!cm.isLocked(index));
47174         }
47175         this.hmenu.show(hd, "tl-bl");
47176     },
47177
47178     handleHdOver : function(e){
47179         var hd = this.findHeaderCell(e.getTarget());
47180         if(hd && !this.headersDisabled){
47181             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47182                this.fly(hd).addClass("x-grid-hd-over");
47183             }
47184         }
47185     },
47186
47187     handleHdOut : function(e){
47188         var hd = this.findHeaderCell(e.getTarget());
47189         if(hd){
47190             this.fly(hd).removeClass("x-grid-hd-over");
47191         }
47192     },
47193
47194     handleSplitDblClick : function(e, t){
47195         var i = this.getCellIndex(t);
47196         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47197             this.autoSizeColumn(i, true);
47198             this.layout();
47199         }
47200     },
47201
47202     render : function(){
47203
47204         var cm = this.cm;
47205         var colCount = cm.getColumnCount();
47206
47207         if(this.grid.monitorWindowResize === true){
47208             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47209         }
47210         var header = this.renderHeaders();
47211         var body = this.templates.body.apply({rows:""});
47212         var html = this.templates.master.apply({
47213             lockedBody: body,
47214             body: body,
47215             lockedHeader: header[0],
47216             header: header[1]
47217         });
47218
47219         //this.updateColumns();
47220
47221         this.grid.getGridEl().dom.innerHTML = html;
47222
47223         this.initElements();
47224
47225         this.scroller.on("scroll", this.handleScroll, this);
47226         this.lockedBody.on("mousewheel", this.handleWheel, this);
47227         this.mainBody.on("mousewheel", this.handleWheel, this);
47228
47229         this.mainHd.on("mouseover", this.handleHdOver, this);
47230         this.mainHd.on("mouseout", this.handleHdOut, this);
47231         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47232                 {delegate: "."+this.splitClass});
47233
47234         this.lockedHd.on("mouseover", this.handleHdOver, this);
47235         this.lockedHd.on("mouseout", this.handleHdOut, this);
47236         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47237                 {delegate: "."+this.splitClass});
47238
47239         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47240             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47241         }
47242
47243         this.updateSplitters();
47244
47245         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47246             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47247             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47248         }
47249
47250         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47251             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47252             this.hmenu.add(
47253                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47254                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47255             );
47256             if(this.grid.enableColLock !== false){
47257                 this.hmenu.add('-',
47258                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47259                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47260                 );
47261             }
47262             if(this.grid.enableColumnHide !== false){
47263
47264                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47265                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47266                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47267
47268                 this.hmenu.add('-',
47269                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47270                 );
47271             }
47272             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47273
47274             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47275         }
47276
47277         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47278             this.dd = new Roo.grid.GridDragZone(this.grid, {
47279                 ddGroup : this.grid.ddGroup || 'GridDD'
47280             });
47281         }
47282
47283         /*
47284         for(var i = 0; i < colCount; i++){
47285             if(cm.isHidden(i)){
47286                 this.hideColumn(i);
47287             }
47288             if(cm.config[i].align){
47289                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47290                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47291             }
47292         }*/
47293         
47294         this.updateHeaderSortState();
47295
47296         this.beforeInitialResize();
47297         this.layout(true);
47298
47299         // two part rendering gives faster view to the user
47300         this.renderPhase2.defer(1, this);
47301     },
47302
47303     renderPhase2 : function(){
47304         // render the rows now
47305         this.refresh();
47306         if(this.grid.autoSizeColumns){
47307             this.autoSizeColumns();
47308         }
47309     },
47310
47311     beforeInitialResize : function(){
47312
47313     },
47314
47315     onColumnSplitterMoved : function(i, w){
47316         this.userResized = true;
47317         var cm = this.grid.colModel;
47318         cm.setColumnWidth(i, w, true);
47319         var cid = cm.getColumnId(i);
47320         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47321         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47322         this.updateSplitters();
47323         this.layout();
47324         this.grid.fireEvent("columnresize", i, w);
47325     },
47326
47327     syncRowHeights : function(startIndex, endIndex){
47328         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47329             startIndex = startIndex || 0;
47330             var mrows = this.getBodyTable().rows;
47331             var lrows = this.getLockedTable().rows;
47332             var len = mrows.length-1;
47333             endIndex = Math.min(endIndex || len, len);
47334             for(var i = startIndex; i <= endIndex; i++){
47335                 var m = mrows[i], l = lrows[i];
47336                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47337                 m.style.height = l.style.height = h + "px";
47338             }
47339         }
47340     },
47341
47342     layout : function(initialRender, is2ndPass){
47343         var g = this.grid;
47344         var auto = g.autoHeight;
47345         var scrollOffset = 16;
47346         var c = g.getGridEl(), cm = this.cm,
47347                 expandCol = g.autoExpandColumn,
47348                 gv = this;
47349         //c.beginMeasure();
47350
47351         if(!c.dom.offsetWidth){ // display:none?
47352             if(initialRender){
47353                 this.lockedWrap.show();
47354                 this.mainWrap.show();
47355             }
47356             return;
47357         }
47358
47359         var hasLock = this.cm.isLocked(0);
47360
47361         var tbh = this.headerPanel.getHeight();
47362         var bbh = this.footerPanel.getHeight();
47363
47364         if(auto){
47365             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47366             var newHeight = ch + c.getBorderWidth("tb");
47367             if(g.maxHeight){
47368                 newHeight = Math.min(g.maxHeight, newHeight);
47369             }
47370             c.setHeight(newHeight);
47371         }
47372
47373         if(g.autoWidth){
47374             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47375         }
47376
47377         var s = this.scroller;
47378
47379         var csize = c.getSize(true);
47380
47381         this.el.setSize(csize.width, csize.height);
47382
47383         this.headerPanel.setWidth(csize.width);
47384         this.footerPanel.setWidth(csize.width);
47385
47386         var hdHeight = this.mainHd.getHeight();
47387         var vw = csize.width;
47388         var vh = csize.height - (tbh + bbh);
47389
47390         s.setSize(vw, vh);
47391
47392         var bt = this.getBodyTable();
47393         var ltWidth = hasLock ?
47394                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47395
47396         var scrollHeight = bt.offsetHeight;
47397         var scrollWidth = ltWidth + bt.offsetWidth;
47398         var vscroll = false, hscroll = false;
47399
47400         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47401
47402         var lw = this.lockedWrap, mw = this.mainWrap;
47403         var lb = this.lockedBody, mb = this.mainBody;
47404
47405         setTimeout(function(){
47406             var t = s.dom.offsetTop;
47407             var w = s.dom.clientWidth,
47408                 h = s.dom.clientHeight;
47409
47410             lw.setTop(t);
47411             lw.setSize(ltWidth, h);
47412
47413             mw.setLeftTop(ltWidth, t);
47414             mw.setSize(w-ltWidth, h);
47415
47416             lb.setHeight(h-hdHeight);
47417             mb.setHeight(h-hdHeight);
47418
47419             if(is2ndPass !== true && !gv.userResized && expandCol){
47420                 // high speed resize without full column calculation
47421                 
47422                 var ci = cm.getIndexById(expandCol);
47423                 if (ci < 0) {
47424                     ci = cm.findColumnIndex(expandCol);
47425                 }
47426                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47427                 var expandId = cm.getColumnId(ci);
47428                 var  tw = cm.getTotalWidth(false);
47429                 var currentWidth = cm.getColumnWidth(ci);
47430                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47431                 if(currentWidth != cw){
47432                     cm.setColumnWidth(ci, cw, true);
47433                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47434                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47435                     gv.updateSplitters();
47436                     gv.layout(false, true);
47437                 }
47438             }
47439
47440             if(initialRender){
47441                 lw.show();
47442                 mw.show();
47443             }
47444             //c.endMeasure();
47445         }, 10);
47446     },
47447
47448     onWindowResize : function(){
47449         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47450             return;
47451         }
47452         this.layout();
47453     },
47454
47455     appendFooter : function(parentEl){
47456         return null;
47457     },
47458
47459     sortAscText : "Sort Ascending",
47460     sortDescText : "Sort Descending",
47461     lockText : "Lock Column",
47462     unlockText : "Unlock Column",
47463     columnsText : "Columns"
47464 });
47465
47466
47467 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47468     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47469     this.proxy.el.addClass('x-grid3-col-dd');
47470 };
47471
47472 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47473     handleMouseDown : function(e){
47474
47475     },
47476
47477     callHandleMouseDown : function(e){
47478         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47479     }
47480 });
47481 /*
47482  * Based on:
47483  * Ext JS Library 1.1.1
47484  * Copyright(c) 2006-2007, Ext JS, LLC.
47485  *
47486  * Originally Released Under LGPL - original licence link has changed is not relivant.
47487  *
47488  * Fork - LGPL
47489  * <script type="text/javascript">
47490  */
47491  
47492 // private
47493 // This is a support class used internally by the Grid components
47494 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47495     this.grid = grid;
47496     this.view = grid.getView();
47497     this.proxy = this.view.resizeProxy;
47498     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47499         "gridSplitters" + this.grid.getGridEl().id, {
47500         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47501     });
47502     this.setHandleElId(Roo.id(hd));
47503     this.setOuterHandleElId(Roo.id(hd2));
47504     this.scroll = false;
47505 };
47506 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47507     fly: Roo.Element.fly,
47508
47509     b4StartDrag : function(x, y){
47510         this.view.headersDisabled = true;
47511         this.proxy.setHeight(this.view.mainWrap.getHeight());
47512         var w = this.cm.getColumnWidth(this.cellIndex);
47513         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47514         this.resetConstraints();
47515         this.setXConstraint(minw, 1000);
47516         this.setYConstraint(0, 0);
47517         this.minX = x - minw;
47518         this.maxX = x + 1000;
47519         this.startPos = x;
47520         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47521     },
47522
47523
47524     handleMouseDown : function(e){
47525         ev = Roo.EventObject.setEvent(e);
47526         var t = this.fly(ev.getTarget());
47527         if(t.hasClass("x-grid-split")){
47528             this.cellIndex = this.view.getCellIndex(t.dom);
47529             this.split = t.dom;
47530             this.cm = this.grid.colModel;
47531             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47532                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47533             }
47534         }
47535     },
47536
47537     endDrag : function(e){
47538         this.view.headersDisabled = false;
47539         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47540         var diff = endX - this.startPos;
47541         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47542     },
47543
47544     autoOffset : function(){
47545         this.setDelta(0,0);
47546     }
47547 });/*
47548  * Based on:
47549  * Ext JS Library 1.1.1
47550  * Copyright(c) 2006-2007, Ext JS, LLC.
47551  *
47552  * Originally Released Under LGPL - original licence link has changed is not relivant.
47553  *
47554  * Fork - LGPL
47555  * <script type="text/javascript">
47556  */
47557  
47558 // private
47559 // This is a support class used internally by the Grid components
47560 Roo.grid.GridDragZone = function(grid, config){
47561     this.view = grid.getView();
47562     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47563     if(this.view.lockedBody){
47564         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47565         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47566     }
47567     this.scroll = false;
47568     this.grid = grid;
47569     this.ddel = document.createElement('div');
47570     this.ddel.className = 'x-grid-dd-wrap';
47571 };
47572
47573 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47574     ddGroup : "GridDD",
47575
47576     getDragData : function(e){
47577         var t = Roo.lib.Event.getTarget(e);
47578         var rowIndex = this.view.findRowIndex(t);
47579         if(rowIndex !== false){
47580             var sm = this.grid.selModel;
47581             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47582               //  sm.mouseDown(e, t);
47583             //}
47584             if (e.hasModifier()){
47585                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47586             }
47587             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47588         }
47589         return false;
47590     },
47591
47592     onInitDrag : function(e){
47593         var data = this.dragData;
47594         this.ddel.innerHTML = this.grid.getDragDropText();
47595         this.proxy.update(this.ddel);
47596         // fire start drag?
47597     },
47598
47599     afterRepair : function(){
47600         this.dragging = false;
47601     },
47602
47603     getRepairXY : function(e, data){
47604         return false;
47605     },
47606
47607     onEndDrag : function(data, e){
47608         // fire end drag?
47609     },
47610
47611     onValidDrop : function(dd, e, id){
47612         // fire drag drop?
47613         this.hideProxy();
47614     },
47615
47616     beforeInvalidDrop : function(e, id){
47617
47618     }
47619 });/*
47620  * Based on:
47621  * Ext JS Library 1.1.1
47622  * Copyright(c) 2006-2007, Ext JS, LLC.
47623  *
47624  * Originally Released Under LGPL - original licence link has changed is not relivant.
47625  *
47626  * Fork - LGPL
47627  * <script type="text/javascript">
47628  */
47629  
47630
47631 /**
47632  * @class Roo.grid.ColumnModel
47633  * @extends Roo.util.Observable
47634  * This is the default implementation of a ColumnModel used by the Grid. It defines
47635  * the columns in the grid.
47636  * <br>Usage:<br>
47637  <pre><code>
47638  var colModel = new Roo.grid.ColumnModel([
47639         {header: "Ticker", width: 60, sortable: true, locked: true},
47640         {header: "Company Name", width: 150, sortable: true},
47641         {header: "Market Cap.", width: 100, sortable: true},
47642         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47643         {header: "Employees", width: 100, sortable: true, resizable: false}
47644  ]);
47645  </code></pre>
47646  * <p>
47647  
47648  * The config options listed for this class are options which may appear in each
47649  * individual column definition.
47650  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47651  * @constructor
47652  * @param {Object} config An Array of column config objects. See this class's
47653  * config objects for details.
47654 */
47655 Roo.grid.ColumnModel = function(config){
47656         /**
47657      * The config passed into the constructor
47658      */
47659     this.config = config;
47660     this.lookup = {};
47661
47662     // if no id, create one
47663     // if the column does not have a dataIndex mapping,
47664     // map it to the order it is in the config
47665     for(var i = 0, len = config.length; i < len; i++){
47666         var c = config[i];
47667         if(typeof c.dataIndex == "undefined"){
47668             c.dataIndex = i;
47669         }
47670         if(typeof c.renderer == "string"){
47671             c.renderer = Roo.util.Format[c.renderer];
47672         }
47673         if(typeof c.id == "undefined"){
47674             c.id = Roo.id();
47675         }
47676         if(c.editor && c.editor.xtype){
47677             c.editor  = Roo.factory(c.editor, Roo.grid);
47678         }
47679         if(c.editor && c.editor.isFormField){
47680             c.editor = new Roo.grid.GridEditor(c.editor);
47681         }
47682         this.lookup[c.id] = c;
47683     }
47684
47685     /**
47686      * The width of columns which have no width specified (defaults to 100)
47687      * @type Number
47688      */
47689     this.defaultWidth = 100;
47690
47691     /**
47692      * Default sortable of columns which have no sortable specified (defaults to false)
47693      * @type Boolean
47694      */
47695     this.defaultSortable = false;
47696
47697     this.addEvents({
47698         /**
47699              * @event widthchange
47700              * Fires when the width of a column changes.
47701              * @param {ColumnModel} this
47702              * @param {Number} columnIndex The column index
47703              * @param {Number} newWidth The new width
47704              */
47705             "widthchange": true,
47706         /**
47707              * @event headerchange
47708              * Fires when the text of a header changes.
47709              * @param {ColumnModel} this
47710              * @param {Number} columnIndex The column index
47711              * @param {Number} newText The new header text
47712              */
47713             "headerchange": true,
47714         /**
47715              * @event hiddenchange
47716              * Fires when a column is hidden or "unhidden".
47717              * @param {ColumnModel} this
47718              * @param {Number} columnIndex The column index
47719              * @param {Boolean} hidden true if hidden, false otherwise
47720              */
47721             "hiddenchange": true,
47722             /**
47723          * @event columnmoved
47724          * Fires when a column is moved.
47725          * @param {ColumnModel} this
47726          * @param {Number} oldIndex
47727          * @param {Number} newIndex
47728          */
47729         "columnmoved" : true,
47730         /**
47731          * @event columlockchange
47732          * Fires when a column's locked state is changed
47733          * @param {ColumnModel} this
47734          * @param {Number} colIndex
47735          * @param {Boolean} locked true if locked
47736          */
47737         "columnlockchange" : true
47738     });
47739     Roo.grid.ColumnModel.superclass.constructor.call(this);
47740 };
47741 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
47742     /**
47743      * @cfg {String} header The header text to display in the Grid view.
47744      */
47745     /**
47746      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
47747      * {@link Roo.data.Record} definition from which to draw the column's value. If not
47748      * specified, the column's index is used as an index into the Record's data Array.
47749      */
47750     /**
47751      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
47752      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
47753      */
47754     /**
47755      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
47756      * Defaults to the value of the {@link #defaultSortable} property.
47757      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
47758      */
47759     /**
47760      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
47761      */
47762     /**
47763      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
47764      */
47765     /**
47766      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
47767      */
47768     /**
47769      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
47770      */
47771     /**
47772      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
47773      * given the cell's data value. See {@link #setRenderer}. If not specified, the
47774      * default renderer uses the raw data value.
47775      */
47776        /**
47777      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
47778      */
47779     /**
47780      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
47781      */
47782
47783     /**
47784      * Returns the id of the column at the specified index.
47785      * @param {Number} index The column index
47786      * @return {String} the id
47787      */
47788     getColumnId : function(index){
47789         return this.config[index].id;
47790     },
47791
47792     /**
47793      * Returns the column for a specified id.
47794      * @param {String} id The column id
47795      * @return {Object} the column
47796      */
47797     getColumnById : function(id){
47798         return this.lookup[id];
47799     },
47800
47801     /**
47802      * Returns the index for a specified column id.
47803      * @param {String} id The column id
47804      * @return {Number} the index, or -1 if not found
47805      */
47806     getIndexById : function(id){
47807         for(var i = 0, len = this.config.length; i < len; i++){
47808             if(this.config[i].id == id){
47809                 return i;
47810             }
47811         }
47812         return -1;
47813     },
47814     /**
47815      * Returns the index for a specified column dataIndex.
47816      * @param {String} dataIndex The column dataIndex
47817      * @return {Number} the index, or -1 if not found
47818      */
47819     
47820     findColumnIndex : function(dataIndex){
47821         for(var i = 0, len = this.config.length; i < len; i++){
47822             if(this.config[i].dataIndex == dataIndex){
47823                 return i;
47824             }
47825         }
47826         return -1;
47827     },
47828     
47829     
47830     moveColumn : function(oldIndex, newIndex){
47831         var c = this.config[oldIndex];
47832         this.config.splice(oldIndex, 1);
47833         this.config.splice(newIndex, 0, c);
47834         this.dataMap = null;
47835         this.fireEvent("columnmoved", this, oldIndex, newIndex);
47836     },
47837
47838     isLocked : function(colIndex){
47839         return this.config[colIndex].locked === true;
47840     },
47841
47842     setLocked : function(colIndex, value, suppressEvent){
47843         if(this.isLocked(colIndex) == value){
47844             return;
47845         }
47846         this.config[colIndex].locked = value;
47847         if(!suppressEvent){
47848             this.fireEvent("columnlockchange", this, colIndex, value);
47849         }
47850     },
47851
47852     getTotalLockedWidth : function(){
47853         var totalWidth = 0;
47854         for(var i = 0; i < this.config.length; i++){
47855             if(this.isLocked(i) && !this.isHidden(i)){
47856                 this.totalWidth += this.getColumnWidth(i);
47857             }
47858         }
47859         return totalWidth;
47860     },
47861
47862     getLockedCount : function(){
47863         for(var i = 0, len = this.config.length; i < len; i++){
47864             if(!this.isLocked(i)){
47865                 return i;
47866             }
47867         }
47868     },
47869
47870     /**
47871      * Returns the number of columns.
47872      * @return {Number}
47873      */
47874     getColumnCount : function(visibleOnly){
47875         if(visibleOnly === true){
47876             var c = 0;
47877             for(var i = 0, len = this.config.length; i < len; i++){
47878                 if(!this.isHidden(i)){
47879                     c++;
47880                 }
47881             }
47882             return c;
47883         }
47884         return this.config.length;
47885     },
47886
47887     /**
47888      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
47889      * @param {Function} fn
47890      * @param {Object} scope (optional)
47891      * @return {Array} result
47892      */
47893     getColumnsBy : function(fn, scope){
47894         var r = [];
47895         for(var i = 0, len = this.config.length; i < len; i++){
47896             var c = this.config[i];
47897             if(fn.call(scope||this, c, i) === true){
47898                 r[r.length] = c;
47899             }
47900         }
47901         return r;
47902     },
47903
47904     /**
47905      * Returns true if the specified column is sortable.
47906      * @param {Number} col The column index
47907      * @return {Boolean}
47908      */
47909     isSortable : function(col){
47910         if(typeof this.config[col].sortable == "undefined"){
47911             return this.defaultSortable;
47912         }
47913         return this.config[col].sortable;
47914     },
47915
47916     /**
47917      * Returns the rendering (formatting) function defined for the column.
47918      * @param {Number} col The column index.
47919      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
47920      */
47921     getRenderer : function(col){
47922         if(!this.config[col].renderer){
47923             return Roo.grid.ColumnModel.defaultRenderer;
47924         }
47925         return this.config[col].renderer;
47926     },
47927
47928     /**
47929      * Sets the rendering (formatting) function for a column.
47930      * @param {Number} col The column index
47931      * @param {Function} fn The function to use to process the cell's raw data
47932      * to return HTML markup for the grid view. The render function is called with
47933      * the following parameters:<ul>
47934      * <li>Data value.</li>
47935      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
47936      * <li>css A CSS style string to apply to the table cell.</li>
47937      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
47938      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
47939      * <li>Row index</li>
47940      * <li>Column index</li>
47941      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
47942      */
47943     setRenderer : function(col, fn){
47944         this.config[col].renderer = fn;
47945     },
47946
47947     /**
47948      * Returns the width for the specified column.
47949      * @param {Number} col The column index
47950      * @return {Number}
47951      */
47952     getColumnWidth : function(col){
47953         return this.config[col].width || this.defaultWidth;
47954     },
47955
47956     /**
47957      * Sets the width for a column.
47958      * @param {Number} col The column index
47959      * @param {Number} width The new width
47960      */
47961     setColumnWidth : function(col, width, suppressEvent){
47962         this.config[col].width = width;
47963         this.totalWidth = null;
47964         if(!suppressEvent){
47965              this.fireEvent("widthchange", this, col, width);
47966         }
47967     },
47968
47969     /**
47970      * Returns the total width of all columns.
47971      * @param {Boolean} includeHidden True to include hidden column widths
47972      * @return {Number}
47973      */
47974     getTotalWidth : function(includeHidden){
47975         if(!this.totalWidth){
47976             this.totalWidth = 0;
47977             for(var i = 0, len = this.config.length; i < len; i++){
47978                 if(includeHidden || !this.isHidden(i)){
47979                     this.totalWidth += this.getColumnWidth(i);
47980                 }
47981             }
47982         }
47983         return this.totalWidth;
47984     },
47985
47986     /**
47987      * Returns the header for the specified column.
47988      * @param {Number} col The column index
47989      * @return {String}
47990      */
47991     getColumnHeader : function(col){
47992         return this.config[col].header;
47993     },
47994
47995     /**
47996      * Sets the header for a column.
47997      * @param {Number} col The column index
47998      * @param {String} header The new header
47999      */
48000     setColumnHeader : function(col, header){
48001         this.config[col].header = header;
48002         this.fireEvent("headerchange", this, col, header);
48003     },
48004
48005     /**
48006      * Returns the tooltip for the specified column.
48007      * @param {Number} col The column index
48008      * @return {String}
48009      */
48010     getColumnTooltip : function(col){
48011             return this.config[col].tooltip;
48012     },
48013     /**
48014      * Sets the tooltip for a column.
48015      * @param {Number} col The column index
48016      * @param {String} tooltip The new tooltip
48017      */
48018     setColumnTooltip : function(col, tooltip){
48019             this.config[col].tooltip = tooltip;
48020     },
48021
48022     /**
48023      * Returns the dataIndex for the specified column.
48024      * @param {Number} col The column index
48025      * @return {Number}
48026      */
48027     getDataIndex : function(col){
48028         return this.config[col].dataIndex;
48029     },
48030
48031     /**
48032      * Sets the dataIndex for a column.
48033      * @param {Number} col The column index
48034      * @param {Number} dataIndex The new dataIndex
48035      */
48036     setDataIndex : function(col, dataIndex){
48037         this.config[col].dataIndex = dataIndex;
48038     },
48039
48040     
48041     
48042     /**
48043      * Returns true if the cell is editable.
48044      * @param {Number} colIndex The column index
48045      * @param {Number} rowIndex The row index
48046      * @return {Boolean}
48047      */
48048     isCellEditable : function(colIndex, rowIndex){
48049         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48050     },
48051
48052     /**
48053      * Returns the editor defined for the cell/column.
48054      * return false or null to disable editing.
48055      * @param {Number} colIndex The column index
48056      * @param {Number} rowIndex The row index
48057      * @return {Object}
48058      */
48059     getCellEditor : function(colIndex, rowIndex){
48060         return this.config[colIndex].editor;
48061     },
48062
48063     /**
48064      * Sets if a column is editable.
48065      * @param {Number} col The column index
48066      * @param {Boolean} editable True if the column is editable
48067      */
48068     setEditable : function(col, editable){
48069         this.config[col].editable = editable;
48070     },
48071
48072
48073     /**
48074      * Returns true if the column is hidden.
48075      * @param {Number} colIndex The column index
48076      * @return {Boolean}
48077      */
48078     isHidden : function(colIndex){
48079         return this.config[colIndex].hidden;
48080     },
48081
48082
48083     /**
48084      * Returns true if the column width cannot be changed
48085      */
48086     isFixed : function(colIndex){
48087         return this.config[colIndex].fixed;
48088     },
48089
48090     /**
48091      * Returns true if the column can be resized
48092      * @return {Boolean}
48093      */
48094     isResizable : function(colIndex){
48095         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48096     },
48097     /**
48098      * Sets if a column is hidden.
48099      * @param {Number} colIndex The column index
48100      * @param {Boolean} hidden True if the column is hidden
48101      */
48102     setHidden : function(colIndex, hidden){
48103         this.config[colIndex].hidden = hidden;
48104         this.totalWidth = null;
48105         this.fireEvent("hiddenchange", this, colIndex, hidden);
48106     },
48107
48108     /**
48109      * Sets the editor for a column.
48110      * @param {Number} col The column index
48111      * @param {Object} editor The editor object
48112      */
48113     setEditor : function(col, editor){
48114         this.config[col].editor = editor;
48115     }
48116 });
48117
48118 Roo.grid.ColumnModel.defaultRenderer = function(value){
48119         if(typeof value == "string" && value.length < 1){
48120             return "&#160;";
48121         }
48122         return value;
48123 };
48124
48125 // Alias for backwards compatibility
48126 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48127 /*
48128  * Based on:
48129  * Ext JS Library 1.1.1
48130  * Copyright(c) 2006-2007, Ext JS, LLC.
48131  *
48132  * Originally Released Under LGPL - original licence link has changed is not relivant.
48133  *
48134  * Fork - LGPL
48135  * <script type="text/javascript">
48136  */
48137
48138 /**
48139  * @class Roo.grid.AbstractSelectionModel
48140  * @extends Roo.util.Observable
48141  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48142  * implemented by descendant classes.  This class should not be directly instantiated.
48143  * @constructor
48144  */
48145 Roo.grid.AbstractSelectionModel = function(){
48146     this.locked = false;
48147     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48148 };
48149
48150 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48151     /** @ignore Called by the grid automatically. Do not call directly. */
48152     init : function(grid){
48153         this.grid = grid;
48154         this.initEvents();
48155     },
48156
48157     /**
48158      * Locks the selections.
48159      */
48160     lock : function(){
48161         this.locked = true;
48162     },
48163
48164     /**
48165      * Unlocks the selections.
48166      */
48167     unlock : function(){
48168         this.locked = false;
48169     },
48170
48171     /**
48172      * Returns true if the selections are locked.
48173      * @return {Boolean}
48174      */
48175     isLocked : function(){
48176         return this.locked;
48177     }
48178 });/*
48179  * Based on:
48180  * Ext JS Library 1.1.1
48181  * Copyright(c) 2006-2007, Ext JS, LLC.
48182  *
48183  * Originally Released Under LGPL - original licence link has changed is not relivant.
48184  *
48185  * Fork - LGPL
48186  * <script type="text/javascript">
48187  */
48188 /**
48189  * @extends Roo.grid.AbstractSelectionModel
48190  * @class Roo.grid.RowSelectionModel
48191  * The default SelectionModel used by {@link Roo.grid.Grid}.
48192  * It supports multiple selections and keyboard selection/navigation. 
48193  * @constructor
48194  * @param {Object} config
48195  */
48196 Roo.grid.RowSelectionModel = function(config){
48197     Roo.apply(this, config);
48198     this.selections = new Roo.util.MixedCollection(false, function(o){
48199         return o.id;
48200     });
48201
48202     this.last = false;
48203     this.lastActive = false;
48204
48205     this.addEvents({
48206         /**
48207              * @event selectionchange
48208              * Fires when the selection changes
48209              * @param {SelectionModel} this
48210              */
48211             "selectionchange" : true,
48212         /**
48213              * @event afterselectionchange
48214              * Fires after the selection changes (eg. by key press or clicking)
48215              * @param {SelectionModel} this
48216              */
48217             "afterselectionchange" : true,
48218         /**
48219              * @event beforerowselect
48220              * Fires when a row is selected being selected, return false to cancel.
48221              * @param {SelectionModel} this
48222              * @param {Number} rowIndex The selected index
48223              * @param {Boolean} keepExisting False if other selections will be cleared
48224              */
48225             "beforerowselect" : true,
48226         /**
48227              * @event rowselect
48228              * Fires when a row is selected.
48229              * @param {SelectionModel} this
48230              * @param {Number} rowIndex The selected index
48231              * @param {Roo.data.Record} r The record
48232              */
48233             "rowselect" : true,
48234         /**
48235              * @event rowdeselect
48236              * Fires when a row is deselected.
48237              * @param {SelectionModel} this
48238              * @param {Number} rowIndex The selected index
48239              */
48240         "rowdeselect" : true
48241     });
48242     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48243     this.locked = false;
48244 };
48245
48246 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48247     /**
48248      * @cfg {Boolean} singleSelect
48249      * True to allow selection of only one row at a time (defaults to false)
48250      */
48251     singleSelect : false,
48252
48253     // private
48254     initEvents : function(){
48255
48256         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48257             this.grid.on("mousedown", this.handleMouseDown, this);
48258         }else{ // allow click to work like normal
48259             this.grid.on("rowclick", this.handleDragableRowClick, this);
48260         }
48261
48262         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48263             "up" : function(e){
48264                 if(!e.shiftKey){
48265                     this.selectPrevious(e.shiftKey);
48266                 }else if(this.last !== false && this.lastActive !== false){
48267                     var last = this.last;
48268                     this.selectRange(this.last,  this.lastActive-1);
48269                     this.grid.getView().focusRow(this.lastActive);
48270                     if(last !== false){
48271                         this.last = last;
48272                     }
48273                 }else{
48274                     this.selectFirstRow();
48275                 }
48276                 this.fireEvent("afterselectionchange", this);
48277             },
48278             "down" : function(e){
48279                 if(!e.shiftKey){
48280                     this.selectNext(e.shiftKey);
48281                 }else if(this.last !== false && this.lastActive !== false){
48282                     var last = this.last;
48283                     this.selectRange(this.last,  this.lastActive+1);
48284                     this.grid.getView().focusRow(this.lastActive);
48285                     if(last !== false){
48286                         this.last = last;
48287                     }
48288                 }else{
48289                     this.selectFirstRow();
48290                 }
48291                 this.fireEvent("afterselectionchange", this);
48292             },
48293             scope: this
48294         });
48295
48296         var view = this.grid.view;
48297         view.on("refresh", this.onRefresh, this);
48298         view.on("rowupdated", this.onRowUpdated, this);
48299         view.on("rowremoved", this.onRemove, this);
48300     },
48301
48302     // private
48303     onRefresh : function(){
48304         var ds = this.grid.dataSource, i, v = this.grid.view;
48305         var s = this.selections;
48306         s.each(function(r){
48307             if((i = ds.indexOfId(r.id)) != -1){
48308                 v.onRowSelect(i);
48309             }else{
48310                 s.remove(r);
48311             }
48312         });
48313     },
48314
48315     // private
48316     onRemove : function(v, index, r){
48317         this.selections.remove(r);
48318     },
48319
48320     // private
48321     onRowUpdated : function(v, index, r){
48322         if(this.isSelected(r)){
48323             v.onRowSelect(index);
48324         }
48325     },
48326
48327     /**
48328      * Select records.
48329      * @param {Array} records The records to select
48330      * @param {Boolean} keepExisting (optional) True to keep existing selections
48331      */
48332     selectRecords : function(records, keepExisting){
48333         if(!keepExisting){
48334             this.clearSelections();
48335         }
48336         var ds = this.grid.dataSource;
48337         for(var i = 0, len = records.length; i < len; i++){
48338             this.selectRow(ds.indexOf(records[i]), true);
48339         }
48340     },
48341
48342     /**
48343      * Gets the number of selected rows.
48344      * @return {Number}
48345      */
48346     getCount : function(){
48347         return this.selections.length;
48348     },
48349
48350     /**
48351      * Selects the first row in the grid.
48352      */
48353     selectFirstRow : function(){
48354         this.selectRow(0);
48355     },
48356
48357     /**
48358      * Select the last row.
48359      * @param {Boolean} keepExisting (optional) True to keep existing selections
48360      */
48361     selectLastRow : function(keepExisting){
48362         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48363     },
48364
48365     /**
48366      * Selects the row immediately following the last selected row.
48367      * @param {Boolean} keepExisting (optional) True to keep existing selections
48368      */
48369     selectNext : function(keepExisting){
48370         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48371             this.selectRow(this.last+1, keepExisting);
48372             this.grid.getView().focusRow(this.last);
48373         }
48374     },
48375
48376     /**
48377      * Selects the row that precedes the last selected row.
48378      * @param {Boolean} keepExisting (optional) True to keep existing selections
48379      */
48380     selectPrevious : function(keepExisting){
48381         if(this.last){
48382             this.selectRow(this.last-1, keepExisting);
48383             this.grid.getView().focusRow(this.last);
48384         }
48385     },
48386
48387     /**
48388      * Returns the selected records
48389      * @return {Array} Array of selected records
48390      */
48391     getSelections : function(){
48392         return [].concat(this.selections.items);
48393     },
48394
48395     /**
48396      * Returns the first selected record.
48397      * @return {Record}
48398      */
48399     getSelected : function(){
48400         return this.selections.itemAt(0);
48401     },
48402
48403
48404     /**
48405      * Clears all selections.
48406      */
48407     clearSelections : function(fast){
48408         if(this.locked) return;
48409         if(fast !== true){
48410             var ds = this.grid.dataSource;
48411             var s = this.selections;
48412             s.each(function(r){
48413                 this.deselectRow(ds.indexOfId(r.id));
48414             }, this);
48415             s.clear();
48416         }else{
48417             this.selections.clear();
48418         }
48419         this.last = false;
48420     },
48421
48422
48423     /**
48424      * Selects all rows.
48425      */
48426     selectAll : function(){
48427         if(this.locked) return;
48428         this.selections.clear();
48429         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48430             this.selectRow(i, true);
48431         }
48432     },
48433
48434     /**
48435      * Returns True if there is a selection.
48436      * @return {Boolean}
48437      */
48438     hasSelection : function(){
48439         return this.selections.length > 0;
48440     },
48441
48442     /**
48443      * Returns True if the specified row is selected.
48444      * @param {Number/Record} record The record or index of the record to check
48445      * @return {Boolean}
48446      */
48447     isSelected : function(index){
48448         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48449         return (r && this.selections.key(r.id) ? true : false);
48450     },
48451
48452     /**
48453      * Returns True if the specified record id is selected.
48454      * @param {String} id The id of record to check
48455      * @return {Boolean}
48456      */
48457     isIdSelected : function(id){
48458         return (this.selections.key(id) ? true : false);
48459     },
48460
48461     // private
48462     handleMouseDown : function(e, t){
48463         var view = this.grid.getView(), rowIndex;
48464         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48465             return;
48466         };
48467         if(e.shiftKey && this.last !== false){
48468             var last = this.last;
48469             this.selectRange(last, rowIndex, e.ctrlKey);
48470             this.last = last; // reset the last
48471             view.focusRow(rowIndex);
48472         }else{
48473             var isSelected = this.isSelected(rowIndex);
48474             if(e.button !== 0 && isSelected){
48475                 view.focusRow(rowIndex);
48476             }else if(e.ctrlKey && isSelected){
48477                 this.deselectRow(rowIndex);
48478             }else if(!isSelected){
48479                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48480                 view.focusRow(rowIndex);
48481             }
48482         }
48483         this.fireEvent("afterselectionchange", this);
48484     },
48485     // private
48486     handleDragableRowClick :  function(grid, rowIndex, e) 
48487     {
48488         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48489             this.selectRow(rowIndex, false);
48490             grid.view.focusRow(rowIndex);
48491              this.fireEvent("afterselectionchange", this);
48492         }
48493     },
48494     
48495     /**
48496      * Selects multiple rows.
48497      * @param {Array} rows Array of the indexes of the row to select
48498      * @param {Boolean} keepExisting (optional) True to keep existing selections
48499      */
48500     selectRows : function(rows, keepExisting){
48501         if(!keepExisting){
48502             this.clearSelections();
48503         }
48504         for(var i = 0, len = rows.length; i < len; i++){
48505             this.selectRow(rows[i], true);
48506         }
48507     },
48508
48509     /**
48510      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48511      * @param {Number} startRow The index of the first row in the range
48512      * @param {Number} endRow The index of the last row in the range
48513      * @param {Boolean} keepExisting (optional) True to retain existing selections
48514      */
48515     selectRange : function(startRow, endRow, keepExisting){
48516         if(this.locked) return;
48517         if(!keepExisting){
48518             this.clearSelections();
48519         }
48520         if(startRow <= endRow){
48521             for(var i = startRow; i <= endRow; i++){
48522                 this.selectRow(i, true);
48523             }
48524         }else{
48525             for(var i = startRow; i >= endRow; i--){
48526                 this.selectRow(i, true);
48527             }
48528         }
48529     },
48530
48531     /**
48532      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48533      * @param {Number} startRow The index of the first row in the range
48534      * @param {Number} endRow The index of the last row in the range
48535      */
48536     deselectRange : function(startRow, endRow, preventViewNotify){
48537         if(this.locked) return;
48538         for(var i = startRow; i <= endRow; i++){
48539             this.deselectRow(i, preventViewNotify);
48540         }
48541     },
48542
48543     /**
48544      * Selects a row.
48545      * @param {Number} row The index of the row to select
48546      * @param {Boolean} keepExisting (optional) True to keep existing selections
48547      */
48548     selectRow : function(index, keepExisting, preventViewNotify){
48549         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48550         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48551             if(!keepExisting || this.singleSelect){
48552                 this.clearSelections();
48553             }
48554             var r = this.grid.dataSource.getAt(index);
48555             this.selections.add(r);
48556             this.last = this.lastActive = index;
48557             if(!preventViewNotify){
48558                 this.grid.getView().onRowSelect(index);
48559             }
48560             this.fireEvent("rowselect", this, index, r);
48561             this.fireEvent("selectionchange", this);
48562         }
48563     },
48564
48565     /**
48566      * Deselects a row.
48567      * @param {Number} row The index of the row to deselect
48568      */
48569     deselectRow : function(index, preventViewNotify){
48570         if(this.locked) return;
48571         if(this.last == index){
48572             this.last = false;
48573         }
48574         if(this.lastActive == index){
48575             this.lastActive = false;
48576         }
48577         var r = this.grid.dataSource.getAt(index);
48578         this.selections.remove(r);
48579         if(!preventViewNotify){
48580             this.grid.getView().onRowDeselect(index);
48581         }
48582         this.fireEvent("rowdeselect", this, index);
48583         this.fireEvent("selectionchange", this);
48584     },
48585
48586     // private
48587     restoreLast : function(){
48588         if(this._last){
48589             this.last = this._last;
48590         }
48591     },
48592
48593     // private
48594     acceptsNav : function(row, col, cm){
48595         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48596     },
48597
48598     // private
48599     onEditorKey : function(field, e){
48600         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48601         if(k == e.TAB){
48602             e.stopEvent();
48603             ed.completeEdit();
48604             if(e.shiftKey){
48605                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48606             }else{
48607                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48608             }
48609         }else if(k == e.ENTER && !e.ctrlKey){
48610             e.stopEvent();
48611             ed.completeEdit();
48612             if(e.shiftKey){
48613                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48614             }else{
48615                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48616             }
48617         }else if(k == e.ESC){
48618             ed.cancelEdit();
48619         }
48620         if(newCell){
48621             g.startEditing(newCell[0], newCell[1]);
48622         }
48623     }
48624 });