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                 if (Roo.debug) 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      * Map (JS 1.6 compatibility)
849      * @param {Function} function  to call
850      */
851     map : function(fun )
852     {
853         var len = this.length >>> 0;
854         if (typeof fun != "function")
855             throw new TypeError();
856
857         var res = new Array(len);
858         var thisp = arguments[1];
859         for (var i = 0; i < len; i++)
860         {
861             if (i in this)
862                 res[i] = fun.call(thisp, this[i], i, this);
863         }
864
865         return res;
866     }
867     
868 });
869
870
871  /*
872  * Based on:
873  * Ext JS Library 1.1.1
874  * Copyright(c) 2006-2007, Ext JS, LLC.
875  *
876  * Originally Released Under LGPL - original licence link has changed is not relivant.
877  *
878  * Fork - LGPL
879  * <script type="text/javascript">
880  */
881
882 /**
883  * @class Date
884  *
885  * The date parsing and format syntax is a subset of
886  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
887  * supported will provide results equivalent to their PHP versions.
888  *
889  * Following is the list of all currently supported formats:
890  *<pre>
891 Sample date:
892 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
893
894 Format  Output      Description
895 ------  ----------  --------------------------------------------------------------
896   d      10         Day of the month, 2 digits with leading zeros
897   D      Wed        A textual representation of a day, three letters
898   j      10         Day of the month without leading zeros
899   l      Wednesday  A full textual representation of the day of the week
900   S      th         English ordinal day of month suffix, 2 chars (use with j)
901   w      3          Numeric representation of the day of the week
902   z      9          The julian date, or day of the year (0-365)
903   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
904   F      January    A full textual representation of the month
905   m      01         Numeric representation of a month, with leading zeros
906   M      Jan        Month name abbreviation, three letters
907   n      1          Numeric representation of a month, without leading zeros
908   t      31         Number of days in the given month
909   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
910   Y      2007       A full numeric representation of a year, 4 digits
911   y      07         A two digit representation of a year
912   a      pm         Lowercase Ante meridiem and Post meridiem
913   A      PM         Uppercase Ante meridiem and Post meridiem
914   g      3          12-hour format of an hour without leading zeros
915   G      15         24-hour format of an hour without leading zeros
916   h      03         12-hour format of an hour with leading zeros
917   H      15         24-hour format of an hour with leading zeros
918   i      05         Minutes with leading zeros
919   s      01         Seconds, with leading zeros
920   O      -0600      Difference to Greenwich time (GMT) in hours
921   T      CST        Timezone setting of the machine running the code
922   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
923 </pre>
924  *
925  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
926  * <pre><code>
927 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
928 document.write(dt.format('Y-m-d'));                         //2007-01-10
929 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
930 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
931  </code></pre>
932  *
933  * Here are some standard date/time patterns that you might find helpful.  They
934  * are not part of the source of Date.js, but to use them you can simply copy this
935  * block of code into any script that is included after Date.js and they will also become
936  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
937  * <pre><code>
938 Date.patterns = {
939     ISO8601Long:"Y-m-d H:i:s",
940     ISO8601Short:"Y-m-d",
941     ShortDate: "n/j/Y",
942     LongDate: "l, F d, Y",
943     FullDateTime: "l, F d, Y g:i:s A",
944     MonthDay: "F d",
945     ShortTime: "g:i A",
946     LongTime: "g:i:s A",
947     SortableDateTime: "Y-m-d\\TH:i:s",
948     UniversalSortableDateTime: "Y-m-d H:i:sO",
949     YearMonth: "F, Y"
950 };
951 </code></pre>
952  *
953  * Example usage:
954  * <pre><code>
955 var dt = new Date();
956 document.write(dt.format(Date.patterns.ShortDate));
957  </code></pre>
958  */
959
960 /*
961  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
962  * They generate precompiled functions from date formats instead of parsing and
963  * processing the pattern every time you format a date.  These functions are available
964  * on every Date object (any javascript function).
965  *
966  * The original article and download are here:
967  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
968  *
969  */
970  
971  
972  // was in core
973 /**
974  Returns the number of milliseconds between this date and date
975  @param {Date} date (optional) Defaults to now
976  @return {Number} The diff in milliseconds
977  @member Date getElapsed
978  */
979 Date.prototype.getElapsed = function(date) {
980         return Math.abs((date || new Date()).getTime()-this.getTime());
981 };
982 // was in date file..
983
984
985 // private
986 Date.parseFunctions = {count:0};
987 // private
988 Date.parseRegexes = [];
989 // private
990 Date.formatFunctions = {count:0};
991
992 // private
993 Date.prototype.dateFormat = function(format) {
994     if (Date.formatFunctions[format] == null) {
995         Date.createNewFormat(format);
996     }
997     var func = Date.formatFunctions[format];
998     return this[func]();
999 };
1000
1001
1002 /**
1003  * Formats a date given the supplied format string
1004  * @param {String} format The format string
1005  * @return {String} The formatted date
1006  * @method
1007  */
1008 Date.prototype.format = Date.prototype.dateFormat;
1009
1010 // private
1011 Date.createNewFormat = function(format) {
1012     var funcName = "format" + Date.formatFunctions.count++;
1013     Date.formatFunctions[format] = funcName;
1014     var code = "Date.prototype." + funcName + " = function(){return ";
1015     var special = false;
1016     var ch = '';
1017     for (var i = 0; i < format.length; ++i) {
1018         ch = format.charAt(i);
1019         if (!special && ch == "\\") {
1020             special = true;
1021         }
1022         else if (special) {
1023             special = false;
1024             code += "'" + String.escape(ch) + "' + ";
1025         }
1026         else {
1027             code += Date.getFormatCode(ch);
1028         }
1029     }
1030     /** eval:var:zzzzzzzzzzzzz */
1031     eval(code.substring(0, code.length - 3) + ";}");
1032 };
1033
1034 // private
1035 Date.getFormatCode = function(character) {
1036     switch (character) {
1037     case "d":
1038         return "String.leftPad(this.getDate(), 2, '0') + ";
1039     case "D":
1040         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1041     case "j":
1042         return "this.getDate() + ";
1043     case "l":
1044         return "Date.dayNames[this.getDay()] + ";
1045     case "S":
1046         return "this.getSuffix() + ";
1047     case "w":
1048         return "this.getDay() + ";
1049     case "z":
1050         return "this.getDayOfYear() + ";
1051     case "W":
1052         return "this.getWeekOfYear() + ";
1053     case "F":
1054         return "Date.monthNames[this.getMonth()] + ";
1055     case "m":
1056         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1057     case "M":
1058         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1059     case "n":
1060         return "(this.getMonth() + 1) + ";
1061     case "t":
1062         return "this.getDaysInMonth() + ";
1063     case "L":
1064         return "(this.isLeapYear() ? 1 : 0) + ";
1065     case "Y":
1066         return "this.getFullYear() + ";
1067     case "y":
1068         return "('' + this.getFullYear()).substring(2, 4) + ";
1069     case "a":
1070         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1071     case "A":
1072         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1073     case "g":
1074         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1075     case "G":
1076         return "this.getHours() + ";
1077     case "h":
1078         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1079     case "H":
1080         return "String.leftPad(this.getHours(), 2, '0') + ";
1081     case "i":
1082         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1083     case "s":
1084         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1085     case "O":
1086         return "this.getGMTOffset() + ";
1087     case "T":
1088         return "this.getTimezone() + ";
1089     case "Z":
1090         return "(this.getTimezoneOffset() * -60) + ";
1091     default:
1092         return "'" + String.escape(character) + "' + ";
1093     }
1094 };
1095
1096 /**
1097  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1098  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1099  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1100  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1101  * string or the parse operation will fail.
1102  * Example Usage:
1103 <pre><code>
1104 //dt = Fri May 25 2007 (current date)
1105 var dt = new Date();
1106
1107 //dt = Thu May 25 2006 (today's month/day in 2006)
1108 dt = Date.parseDate("2006", "Y");
1109
1110 //dt = Sun Jan 15 2006 (all date parts specified)
1111 dt = Date.parseDate("2006-1-15", "Y-m-d");
1112
1113 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1114 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1115 </code></pre>
1116  * @param {String} input The unparsed date as a string
1117  * @param {String} format The format the date is in
1118  * @return {Date} The parsed date
1119  * @static
1120  */
1121 Date.parseDate = function(input, format) {
1122     if (Date.parseFunctions[format] == null) {
1123         Date.createParser(format);
1124     }
1125     var func = Date.parseFunctions[format];
1126     return Date[func](input);
1127 };
1128 /**
1129  * @private
1130  */
1131 Date.createParser = function(format) {
1132     var funcName = "parse" + Date.parseFunctions.count++;
1133     var regexNum = Date.parseRegexes.length;
1134     var currentGroup = 1;
1135     Date.parseFunctions[format] = funcName;
1136
1137     var code = "Date." + funcName + " = function(input){\n"
1138         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1139         + "var d = new Date();\n"
1140         + "y = d.getFullYear();\n"
1141         + "m = d.getMonth();\n"
1142         + "d = d.getDate();\n"
1143         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1144         + "if (results && results.length > 0) {";
1145     var regex = "";
1146
1147     var special = false;
1148     var ch = '';
1149     for (var i = 0; i < format.length; ++i) {
1150         ch = format.charAt(i);
1151         if (!special && ch == "\\") {
1152             special = true;
1153         }
1154         else if (special) {
1155             special = false;
1156             regex += String.escape(ch);
1157         }
1158         else {
1159             var obj = Date.formatCodeToRegex(ch, currentGroup);
1160             currentGroup += obj.g;
1161             regex += obj.s;
1162             if (obj.g && obj.c) {
1163                 code += obj.c;
1164             }
1165         }
1166     }
1167
1168     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1169         + "{v = new Date(y, m, d, h, i, s);}\n"
1170         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1171         + "{v = new Date(y, m, d, h, i);}\n"
1172         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1173         + "{v = new Date(y, m, d, h);}\n"
1174         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1175         + "{v = new Date(y, m, d);}\n"
1176         + "else if (y >= 0 && m >= 0)\n"
1177         + "{v = new Date(y, m);}\n"
1178         + "else if (y >= 0)\n"
1179         + "{v = new Date(y);}\n"
1180         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1181         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1182         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1183         + ";}";
1184
1185     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1186     /** eval:var:zzzzzzzzzzzzz */
1187     eval(code);
1188 };
1189
1190 // private
1191 Date.formatCodeToRegex = function(character, currentGroup) {
1192     switch (character) {
1193     case "D":
1194         return {g:0,
1195         c:null,
1196         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1197     case "j":
1198         return {g:1,
1199             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1200             s:"(\\d{1,2})"}; // day of month without leading zeroes
1201     case "d":
1202         return {g:1,
1203             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1204             s:"(\\d{2})"}; // day of month with leading zeroes
1205     case "l":
1206         return {g:0,
1207             c:null,
1208             s:"(?:" + Date.dayNames.join("|") + ")"};
1209     case "S":
1210         return {g:0,
1211             c:null,
1212             s:"(?:st|nd|rd|th)"};
1213     case "w":
1214         return {g:0,
1215             c:null,
1216             s:"\\d"};
1217     case "z":
1218         return {g:0,
1219             c:null,
1220             s:"(?:\\d{1,3})"};
1221     case "W":
1222         return {g:0,
1223             c:null,
1224             s:"(?:\\d{2})"};
1225     case "F":
1226         return {g:1,
1227             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1228             s:"(" + Date.monthNames.join("|") + ")"};
1229     case "M":
1230         return {g:1,
1231             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1232             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1233     case "n":
1234         return {g:1,
1235             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1236             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1237     case "m":
1238         return {g:1,
1239             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1240             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1241     case "t":
1242         return {g:0,
1243             c:null,
1244             s:"\\d{1,2}"};
1245     case "L":
1246         return {g:0,
1247             c:null,
1248             s:"(?:1|0)"};
1249     case "Y":
1250         return {g:1,
1251             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1252             s:"(\\d{4})"};
1253     case "y":
1254         return {g:1,
1255             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1256                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1257             s:"(\\d{1,2})"};
1258     case "a":
1259         return {g:1,
1260             c:"if (results[" + currentGroup + "] == 'am') {\n"
1261                 + "if (h == 12) { h = 0; }\n"
1262                 + "} else { if (h < 12) { h += 12; }}",
1263             s:"(am|pm)"};
1264     case "A":
1265         return {g:1,
1266             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1267                 + "if (h == 12) { h = 0; }\n"
1268                 + "} else { if (h < 12) { h += 12; }}",
1269             s:"(AM|PM)"};
1270     case "g":
1271     case "G":
1272         return {g:1,
1273             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1274             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1275     case "h":
1276     case "H":
1277         return {g:1,
1278             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1279             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1280     case "i":
1281         return {g:1,
1282             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1283             s:"(\\d{2})"};
1284     case "s":
1285         return {g:1,
1286             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{2})"};
1288     case "O":
1289         return {g:1,
1290             c:[
1291                 "o = results[", currentGroup, "];\n",
1292                 "var sn = o.substring(0,1);\n", // get + / - sign
1293                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1294                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1295                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1296                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1297             ].join(""),
1298             s:"([+\-]\\d{4})"};
1299     case "T":
1300         return {g:0,
1301             c:null,
1302             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1303     case "Z":
1304         return {g:1,
1305             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1306                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1307             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1308     default:
1309         return {g:0,
1310             c:null,
1311             s:String.escape(character)};
1312     }
1313 };
1314
1315 /**
1316  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1317  * @return {String} The abbreviated timezone name (e.g. 'CST')
1318  */
1319 Date.prototype.getTimezone = function() {
1320     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1321 };
1322
1323 /**
1324  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1325  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1326  */
1327 Date.prototype.getGMTOffset = function() {
1328     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1329         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1330         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1331 };
1332
1333 /**
1334  * Get the numeric day number of the year, adjusted for leap year.
1335  * @return {Number} 0 through 364 (365 in leap years)
1336  */
1337 Date.prototype.getDayOfYear = function() {
1338     var num = 0;
1339     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1340     for (var i = 0; i < this.getMonth(); ++i) {
1341         num += Date.daysInMonth[i];
1342     }
1343     return num + this.getDate() - 1;
1344 };
1345
1346 /**
1347  * Get the string representation of the numeric week number of the year
1348  * (equivalent to the format specifier 'W').
1349  * @return {String} '00' through '52'
1350  */
1351 Date.prototype.getWeekOfYear = function() {
1352     // Skip to Thursday of this week
1353     var now = this.getDayOfYear() + (4 - this.getDay());
1354     // Find the first Thursday of the year
1355     var jan1 = new Date(this.getFullYear(), 0, 1);
1356     var then = (7 - jan1.getDay() + 4);
1357     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1358 };
1359
1360 /**
1361  * Whether or not the current date is in a leap year.
1362  * @return {Boolean} True if the current date is in a leap year, else false
1363  */
1364 Date.prototype.isLeapYear = function() {
1365     var year = this.getFullYear();
1366     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1367 };
1368
1369 /**
1370  * Get the first day of the current month, adjusted for leap year.  The returned value
1371  * is the numeric day index within the week (0-6) which can be used in conjunction with
1372  * the {@link #monthNames} array to retrieve the textual day name.
1373  * Example:
1374  *<pre><code>
1375 var dt = new Date('1/10/2007');
1376 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1377 </code></pre>
1378  * @return {Number} The day number (0-6)
1379  */
1380 Date.prototype.getFirstDayOfMonth = function() {
1381     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1382     return (day < 0) ? (day + 7) : day;
1383 };
1384
1385 /**
1386  * Get the last day of the current month, adjusted for leap year.  The returned value
1387  * is the numeric day index within the week (0-6) which can be used in conjunction with
1388  * the {@link #monthNames} array to retrieve the textual day name.
1389  * Example:
1390  *<pre><code>
1391 var dt = new Date('1/10/2007');
1392 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1393 </code></pre>
1394  * @return {Number} The day number (0-6)
1395  */
1396 Date.prototype.getLastDayOfMonth = function() {
1397     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1398     return (day < 0) ? (day + 7) : day;
1399 };
1400
1401
1402 /**
1403  * Get the first date of this date's month
1404  * @return {Date}
1405  */
1406 Date.prototype.getFirstDateOfMonth = function() {
1407     return new Date(this.getFullYear(), this.getMonth(), 1);
1408 };
1409
1410 /**
1411  * Get the last date of this date's month
1412  * @return {Date}
1413  */
1414 Date.prototype.getLastDateOfMonth = function() {
1415     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1416 };
1417 /**
1418  * Get the number of days in the current month, adjusted for leap year.
1419  * @return {Number} The number of days in the month
1420  */
1421 Date.prototype.getDaysInMonth = function() {
1422     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1423     return Date.daysInMonth[this.getMonth()];
1424 };
1425
1426 /**
1427  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1428  * @return {String} 'st, 'nd', 'rd' or 'th'
1429  */
1430 Date.prototype.getSuffix = function() {
1431     switch (this.getDate()) {
1432         case 1:
1433         case 21:
1434         case 31:
1435             return "st";
1436         case 2:
1437         case 22:
1438             return "nd";
1439         case 3:
1440         case 23:
1441             return "rd";
1442         default:
1443             return "th";
1444     }
1445 };
1446
1447 // private
1448 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1449
1450 /**
1451  * An array of textual month names.
1452  * Override these values for international dates, for example...
1453  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1454  * @type Array
1455  * @static
1456  */
1457 Date.monthNames =
1458    ["January",
1459     "February",
1460     "March",
1461     "April",
1462     "May",
1463     "June",
1464     "July",
1465     "August",
1466     "September",
1467     "October",
1468     "November",
1469     "December"];
1470
1471 /**
1472  * An array of textual day names.
1473  * Override these values for international dates, for example...
1474  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1475  * @type Array
1476  * @static
1477  */
1478 Date.dayNames =
1479    ["Sunday",
1480     "Monday",
1481     "Tuesday",
1482     "Wednesday",
1483     "Thursday",
1484     "Friday",
1485     "Saturday"];
1486
1487 // private
1488 Date.y2kYear = 50;
1489 // private
1490 Date.monthNumbers = {
1491     Jan:0,
1492     Feb:1,
1493     Mar:2,
1494     Apr:3,
1495     May:4,
1496     Jun:5,
1497     Jul:6,
1498     Aug:7,
1499     Sep:8,
1500     Oct:9,
1501     Nov:10,
1502     Dec:11};
1503
1504 /**
1505  * Creates and returns a new Date instance with the exact same date value as the called instance.
1506  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1507  * variable will also be changed.  When the intention is to create a new variable that will not
1508  * modify the original instance, you should create a clone.
1509  *
1510  * Example of correctly cloning a date:
1511  * <pre><code>
1512 //wrong way:
1513 var orig = new Date('10/1/2006');
1514 var copy = orig;
1515 copy.setDate(5);
1516 document.write(orig);  //returns 'Thu Oct 05 2006'!
1517
1518 //correct way:
1519 var orig = new Date('10/1/2006');
1520 var copy = orig.clone();
1521 copy.setDate(5);
1522 document.write(orig);  //returns 'Thu Oct 01 2006'
1523 </code></pre>
1524  * @return {Date} The new Date instance
1525  */
1526 Date.prototype.clone = function() {
1527         return new Date(this.getTime());
1528 };
1529
1530 /**
1531  * Clears any time information from this date
1532  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1533  @return {Date} this or the clone
1534  */
1535 Date.prototype.clearTime = function(clone){
1536     if(clone){
1537         return this.clone().clearTime();
1538     }
1539     this.setHours(0);
1540     this.setMinutes(0);
1541     this.setSeconds(0);
1542     this.setMilliseconds(0);
1543     return this;
1544 };
1545
1546 // private
1547 // safari setMonth is broken
1548 if(Roo.isSafari){
1549     Date.brokenSetMonth = Date.prototype.setMonth;
1550         Date.prototype.setMonth = function(num){
1551                 if(num <= -1){
1552                         var n = Math.ceil(-num);
1553                         var back_year = Math.ceil(n/12);
1554                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1555                         this.setFullYear(this.getFullYear() - back_year);
1556                         return Date.brokenSetMonth.call(this, month);
1557                 } else {
1558                         return Date.brokenSetMonth.apply(this, arguments);
1559                 }
1560         };
1561 }
1562
1563 /** Date interval constant 
1564 * @static 
1565 * @type String */
1566 Date.MILLI = "ms";
1567 /** Date interval constant 
1568 * @static 
1569 * @type String */
1570 Date.SECOND = "s";
1571 /** Date interval constant 
1572 * @static 
1573 * @type String */
1574 Date.MINUTE = "mi";
1575 /** Date interval constant 
1576 * @static 
1577 * @type String */
1578 Date.HOUR = "h";
1579 /** Date interval constant 
1580 * @static 
1581 * @type String */
1582 Date.DAY = "d";
1583 /** Date interval constant 
1584 * @static 
1585 * @type String */
1586 Date.MONTH = "mo";
1587 /** Date interval constant 
1588 * @static 
1589 * @type String */
1590 Date.YEAR = "y";
1591
1592 /**
1593  * Provides a convenient method of performing basic date arithmetic.  This method
1594  * does not modify the Date instance being called - it creates and returns
1595  * a new Date instance containing the resulting date value.
1596  *
1597  * Examples:
1598  * <pre><code>
1599 //Basic usage:
1600 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1601 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1602
1603 //Negative values will subtract correctly:
1604 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1605 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1606
1607 //You can even chain several calls together in one line!
1608 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1609 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1610  </code></pre>
1611  *
1612  * @param {String} interval   A valid date interval enum value
1613  * @param {Number} value      The amount to add to the current date
1614  * @return {Date} The new Date instance
1615  */
1616 Date.prototype.add = function(interval, value){
1617   var d = this.clone();
1618   if (!interval || value === 0) return d;
1619   switch(interval.toLowerCase()){
1620     case Date.MILLI:
1621       d.setMilliseconds(this.getMilliseconds() + value);
1622       break;
1623     case Date.SECOND:
1624       d.setSeconds(this.getSeconds() + value);
1625       break;
1626     case Date.MINUTE:
1627       d.setMinutes(this.getMinutes() + value);
1628       break;
1629     case Date.HOUR:
1630       d.setHours(this.getHours() + value);
1631       break;
1632     case Date.DAY:
1633       d.setDate(this.getDate() + value);
1634       break;
1635     case Date.MONTH:
1636       var day = this.getDate();
1637       if(day > 28){
1638           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1639       }
1640       d.setDate(day);
1641       d.setMonth(this.getMonth() + value);
1642       break;
1643     case Date.YEAR:
1644       d.setFullYear(this.getFullYear() + value);
1645       break;
1646   }
1647   return d;
1648 };/*
1649  * Based on:
1650  * Ext JS Library 1.1.1
1651  * Copyright(c) 2006-2007, Ext JS, LLC.
1652  *
1653  * Originally Released Under LGPL - original licence link has changed is not relivant.
1654  *
1655  * Fork - LGPL
1656  * <script type="text/javascript">
1657  */
1658
1659 Roo.lib.Dom = {
1660     getViewWidth : function(full) {
1661         return full ? this.getDocumentWidth() : this.getViewportWidth();
1662     },
1663
1664     getViewHeight : function(full) {
1665         return full ? this.getDocumentHeight() : this.getViewportHeight();
1666     },
1667
1668     getDocumentHeight: function() {
1669         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1670         return Math.max(scrollHeight, this.getViewportHeight());
1671     },
1672
1673     getDocumentWidth: function() {
1674         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1675         return Math.max(scrollWidth, this.getViewportWidth());
1676     },
1677
1678     getViewportHeight: function() {
1679         var height = self.innerHeight;
1680         var mode = document.compatMode;
1681
1682         if ((mode || Roo.isIE) && !Roo.isOpera) {
1683             height = (mode == "CSS1Compat") ?
1684                      document.documentElement.clientHeight :
1685                      document.body.clientHeight;
1686         }
1687
1688         return height;
1689     },
1690
1691     getViewportWidth: function() {
1692         var width = self.innerWidth;
1693         var mode = document.compatMode;
1694
1695         if (mode || Roo.isIE) {
1696             width = (mode == "CSS1Compat") ?
1697                     document.documentElement.clientWidth :
1698                     document.body.clientWidth;
1699         }
1700         return width;
1701     },
1702
1703     isAncestor : function(p, c) {
1704         p = Roo.getDom(p);
1705         c = Roo.getDom(c);
1706         if (!p || !c) {
1707             return false;
1708         }
1709
1710         if (p.contains && !Roo.isSafari) {
1711             return p.contains(c);
1712         } else if (p.compareDocumentPosition) {
1713             return !!(p.compareDocumentPosition(c) & 16);
1714         } else {
1715             var parent = c.parentNode;
1716             while (parent) {
1717                 if (parent == p) {
1718                     return true;
1719                 }
1720                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1721                     return false;
1722                 }
1723                 parent = parent.parentNode;
1724             }
1725             return false;
1726         }
1727     },
1728
1729     getRegion : function(el) {
1730         return Roo.lib.Region.getRegion(el);
1731     },
1732
1733     getY : function(el) {
1734         return this.getXY(el)[1];
1735     },
1736
1737     getX : function(el) {
1738         return this.getXY(el)[0];
1739     },
1740
1741     getXY : function(el) {
1742         var p, pe, b, scroll, bd = document.body;
1743         el = Roo.getDom(el);
1744         var fly = Roo.lib.AnimBase.fly;
1745         if (el.getBoundingClientRect) {
1746             b = el.getBoundingClientRect();
1747             scroll = fly(document).getScroll();
1748             return [b.left + scroll.left, b.top + scroll.top];
1749         }
1750         var x = 0, y = 0;
1751
1752         p = el;
1753
1754         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1755
1756         while (p) {
1757
1758             x += p.offsetLeft;
1759             y += p.offsetTop;
1760
1761             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1762                 hasAbsolute = true;
1763             }
1764
1765             if (Roo.isGecko) {
1766                 pe = fly(p);
1767
1768                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1769                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1770
1771
1772                 x += bl;
1773                 y += bt;
1774
1775
1776                 if (p != el && pe.getStyle('overflow') != 'visible') {
1777                     x += bl;
1778                     y += bt;
1779                 }
1780             }
1781             p = p.offsetParent;
1782         }
1783
1784         if (Roo.isSafari && hasAbsolute) {
1785             x -= bd.offsetLeft;
1786             y -= bd.offsetTop;
1787         }
1788
1789         if (Roo.isGecko && !hasAbsolute) {
1790             var dbd = fly(bd);
1791             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1792             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1793         }
1794
1795         p = el.parentNode;
1796         while (p && p != bd) {
1797             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1798                 x -= p.scrollLeft;
1799                 y -= p.scrollTop;
1800             }
1801             p = p.parentNode;
1802         }
1803         return [x, y];
1804     },
1805  
1806   
1807
1808
1809     setXY : function(el, xy) {
1810         el = Roo.fly(el, '_setXY');
1811         el.position();
1812         var pts = el.translatePoints(xy);
1813         if (xy[0] !== false) {
1814             el.dom.style.left = pts.left + "px";
1815         }
1816         if (xy[1] !== false) {
1817             el.dom.style.top = pts.top + "px";
1818         }
1819     },
1820
1821     setX : function(el, x) {
1822         this.setXY(el, [x, false]);
1823     },
1824
1825     setY : function(el, y) {
1826         this.setXY(el, [false, y]);
1827     }
1828 };
1829 /*
1830  * Portions of this file are based on pieces of Yahoo User Interface Library
1831  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1832  * YUI licensed under the BSD License:
1833  * http://developer.yahoo.net/yui/license.txt
1834  * <script type="text/javascript">
1835  *
1836  */
1837
1838 Roo.lib.Event = function() {
1839     var loadComplete = false;
1840     var listeners = [];
1841     var unloadListeners = [];
1842     var retryCount = 0;
1843     var onAvailStack = [];
1844     var counter = 0;
1845     var lastError = null;
1846
1847     return {
1848         POLL_RETRYS: 200,
1849         POLL_INTERVAL: 20,
1850         EL: 0,
1851         TYPE: 1,
1852         FN: 2,
1853         WFN: 3,
1854         OBJ: 3,
1855         ADJ_SCOPE: 4,
1856         _interval: null,
1857
1858         startInterval: function() {
1859             if (!this._interval) {
1860                 var self = this;
1861                 var callback = function() {
1862                     self._tryPreloadAttach();
1863                 };
1864                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1865
1866             }
1867         },
1868
1869         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1870             onAvailStack.push({ id:         p_id,
1871                 fn:         p_fn,
1872                 obj:        p_obj,
1873                 override:   p_override,
1874                 checkReady: false    });
1875
1876             retryCount = this.POLL_RETRYS;
1877             this.startInterval();
1878         },
1879
1880
1881         addListener: function(el, eventName, fn) {
1882             el = Roo.getDom(el);
1883             if (!el || !fn) {
1884                 return false;
1885             }
1886
1887             if ("unload" == eventName) {
1888                 unloadListeners[unloadListeners.length] =
1889                 [el, eventName, fn];
1890                 return true;
1891             }
1892
1893             var wrappedFn = function(e) {
1894                 return fn(Roo.lib.Event.getEvent(e));
1895             };
1896
1897             var li = [el, eventName, fn, wrappedFn];
1898
1899             var index = listeners.length;
1900             listeners[index] = li;
1901
1902             this.doAdd(el, eventName, wrappedFn, false);
1903             return true;
1904
1905         },
1906
1907
1908         removeListener: function(el, eventName, fn) {
1909             var i, len;
1910
1911             el = Roo.getDom(el);
1912
1913             if(!fn) {
1914                 return this.purgeElement(el, false, eventName);
1915             }
1916
1917
1918             if ("unload" == eventName) {
1919
1920                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1921                     var li = unloadListeners[i];
1922                     if (li &&
1923                         li[0] == el &&
1924                         li[1] == eventName &&
1925                         li[2] == fn) {
1926                         unloadListeners.splice(i, 1);
1927                         return true;
1928                     }
1929                 }
1930
1931                 return false;
1932             }
1933
1934             var cacheItem = null;
1935
1936
1937             var index = arguments[3];
1938
1939             if ("undefined" == typeof index) {
1940                 index = this._getCacheIndex(el, eventName, fn);
1941             }
1942
1943             if (index >= 0) {
1944                 cacheItem = listeners[index];
1945             }
1946
1947             if (!el || !cacheItem) {
1948                 return false;
1949             }
1950
1951             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1952
1953             delete listeners[index][this.WFN];
1954             delete listeners[index][this.FN];
1955             listeners.splice(index, 1);
1956
1957             return true;
1958
1959         },
1960
1961
1962         getTarget: function(ev, resolveTextNode) {
1963             ev = ev.browserEvent || ev;
1964             var t = ev.target || ev.srcElement;
1965             return this.resolveTextNode(t);
1966         },
1967
1968
1969         resolveTextNode: function(node) {
1970             if (Roo.isSafari && node && 3 == node.nodeType) {
1971                 return node.parentNode;
1972             } else {
1973                 return node;
1974             }
1975         },
1976
1977
1978         getPageX: function(ev) {
1979             ev = ev.browserEvent || ev;
1980             var x = ev.pageX;
1981             if (!x && 0 !== x) {
1982                 x = ev.clientX || 0;
1983
1984                 if (Roo.isIE) {
1985                     x += this.getScroll()[1];
1986                 }
1987             }
1988
1989             return x;
1990         },
1991
1992
1993         getPageY: function(ev) {
1994             ev = ev.browserEvent || ev;
1995             var y = ev.pageY;
1996             if (!y && 0 !== y) {
1997                 y = ev.clientY || 0;
1998
1999                 if (Roo.isIE) {
2000                     y += this.getScroll()[0];
2001                 }
2002             }
2003
2004
2005             return y;
2006         },
2007
2008
2009         getXY: function(ev) {
2010             ev = ev.browserEvent || ev;
2011             return [this.getPageX(ev), this.getPageY(ev)];
2012         },
2013
2014
2015         getRelatedTarget: function(ev) {
2016             ev = ev.browserEvent || ev;
2017             var t = ev.relatedTarget;
2018             if (!t) {
2019                 if (ev.type == "mouseout") {
2020                     t = ev.toElement;
2021                 } else if (ev.type == "mouseover") {
2022                     t = ev.fromElement;
2023                 }
2024             }
2025
2026             return this.resolveTextNode(t);
2027         },
2028
2029
2030         getTime: function(ev) {
2031             ev = ev.browserEvent || ev;
2032             if (!ev.time) {
2033                 var t = new Date().getTime();
2034                 try {
2035                     ev.time = t;
2036                 } catch(ex) {
2037                     this.lastError = ex;
2038                     return t;
2039                 }
2040             }
2041
2042             return ev.time;
2043         },
2044
2045
2046         stopEvent: function(ev) {
2047             this.stopPropagation(ev);
2048             this.preventDefault(ev);
2049         },
2050
2051
2052         stopPropagation: function(ev) {
2053             ev = ev.browserEvent || ev;
2054             if (ev.stopPropagation) {
2055                 ev.stopPropagation();
2056             } else {
2057                 ev.cancelBubble = true;
2058             }
2059         },
2060
2061
2062         preventDefault: function(ev) {
2063             ev = ev.browserEvent || ev;
2064             if(ev.preventDefault) {
2065                 ev.preventDefault();
2066             } else {
2067                 ev.returnValue = false;
2068             }
2069         },
2070
2071
2072         getEvent: function(e) {
2073             var ev = e || window.event;
2074             if (!ev) {
2075                 var c = this.getEvent.caller;
2076                 while (c) {
2077                     ev = c.arguments[0];
2078                     if (ev && Event == ev.constructor) {
2079                         break;
2080                     }
2081                     c = c.caller;
2082                 }
2083             }
2084             return ev;
2085         },
2086
2087
2088         getCharCode: function(ev) {
2089             ev = ev.browserEvent || ev;
2090             return ev.charCode || ev.keyCode || 0;
2091         },
2092
2093
2094         _getCacheIndex: function(el, eventName, fn) {
2095             for (var i = 0,len = listeners.length; i < len; ++i) {
2096                 var li = listeners[i];
2097                 if (li &&
2098                     li[this.FN] == fn &&
2099                     li[this.EL] == el &&
2100                     li[this.TYPE] == eventName) {
2101                     return i;
2102                 }
2103             }
2104
2105             return -1;
2106         },
2107
2108
2109         elCache: {},
2110
2111
2112         getEl: function(id) {
2113             return document.getElementById(id);
2114         },
2115
2116
2117         clearCache: function() {
2118         },
2119
2120
2121         _load: function(e) {
2122             loadComplete = true;
2123             var EU = Roo.lib.Event;
2124
2125
2126             if (Roo.isIE) {
2127                 EU.doRemove(window, "load", EU._load);
2128             }
2129         },
2130
2131
2132         _tryPreloadAttach: function() {
2133
2134             if (this.locked) {
2135                 return false;
2136             }
2137
2138             this.locked = true;
2139
2140
2141             var tryAgain = !loadComplete;
2142             if (!tryAgain) {
2143                 tryAgain = (retryCount > 0);
2144             }
2145
2146
2147             var notAvail = [];
2148             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2149                 var item = onAvailStack[i];
2150                 if (item) {
2151                     var el = this.getEl(item.id);
2152
2153                     if (el) {
2154                         if (!item.checkReady ||
2155                             loadComplete ||
2156                             el.nextSibling ||
2157                             (document && document.body)) {
2158
2159                             var scope = el;
2160                             if (item.override) {
2161                                 if (item.override === true) {
2162                                     scope = item.obj;
2163                                 } else {
2164                                     scope = item.override;
2165                                 }
2166                             }
2167                             item.fn.call(scope, item.obj);
2168                             onAvailStack[i] = null;
2169                         }
2170                     } else {
2171                         notAvail.push(item);
2172                     }
2173                 }
2174             }
2175
2176             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2177
2178             if (tryAgain) {
2179
2180                 this.startInterval();
2181             } else {
2182                 clearInterval(this._interval);
2183                 this._interval = null;
2184             }
2185
2186             this.locked = false;
2187
2188             return true;
2189
2190         },
2191
2192
2193         purgeElement: function(el, recurse, eventName) {
2194             var elListeners = this.getListeners(el, eventName);
2195             if (elListeners) {
2196                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2197                     var l = elListeners[i];
2198                     this.removeListener(el, l.type, l.fn);
2199                 }
2200             }
2201
2202             if (recurse && el && el.childNodes) {
2203                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2204                     this.purgeElement(el.childNodes[i], recurse, eventName);
2205                 }
2206             }
2207         },
2208
2209
2210         getListeners: function(el, eventName) {
2211             var results = [], searchLists;
2212             if (!eventName) {
2213                 searchLists = [listeners, unloadListeners];
2214             } else if (eventName == "unload") {
2215                 searchLists = [unloadListeners];
2216             } else {
2217                 searchLists = [listeners];
2218             }
2219
2220             for (var j = 0; j < searchLists.length; ++j) {
2221                 var searchList = searchLists[j];
2222                 if (searchList && searchList.length > 0) {
2223                     for (var i = 0,len = searchList.length; i < len; ++i) {
2224                         var l = searchList[i];
2225                         if (l && l[this.EL] === el &&
2226                             (!eventName || eventName === l[this.TYPE])) {
2227                             results.push({
2228                                 type:   l[this.TYPE],
2229                                 fn:     l[this.FN],
2230                                 obj:    l[this.OBJ],
2231                                 adjust: l[this.ADJ_SCOPE],
2232                                 index:  i
2233                             });
2234                         }
2235                     }
2236                 }
2237             }
2238
2239             return (results.length) ? results : null;
2240         },
2241
2242
2243         _unload: function(e) {
2244
2245             var EU = Roo.lib.Event, i, j, l, len, index;
2246
2247             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2248                 l = unloadListeners[i];
2249                 if (l) {
2250                     var scope = window;
2251                     if (l[EU.ADJ_SCOPE]) {
2252                         if (l[EU.ADJ_SCOPE] === true) {
2253                             scope = l[EU.OBJ];
2254                         } else {
2255                             scope = l[EU.ADJ_SCOPE];
2256                         }
2257                     }
2258                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2259                     unloadListeners[i] = null;
2260                     l = null;
2261                     scope = null;
2262                 }
2263             }
2264
2265             unloadListeners = null;
2266
2267             if (listeners && listeners.length > 0) {
2268                 j = listeners.length;
2269                 while (j) {
2270                     index = j - 1;
2271                     l = listeners[index];
2272                     if (l) {
2273                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2274                                 l[EU.FN], index);
2275                     }
2276                     j = j - 1;
2277                 }
2278                 l = null;
2279
2280                 EU.clearCache();
2281             }
2282
2283             EU.doRemove(window, "unload", EU._unload);
2284
2285         },
2286
2287
2288         getScroll: function() {
2289             var dd = document.documentElement, db = document.body;
2290             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2291                 return [dd.scrollTop, dd.scrollLeft];
2292             } else if (db) {
2293                 return [db.scrollTop, db.scrollLeft];
2294             } else {
2295                 return [0, 0];
2296             }
2297         },
2298
2299
2300         doAdd: function () {
2301             if (window.addEventListener) {
2302                 return function(el, eventName, fn, capture) {
2303                     el.addEventListener(eventName, fn, (capture));
2304                 };
2305             } else if (window.attachEvent) {
2306                 return function(el, eventName, fn, capture) {
2307                     el.attachEvent("on" + eventName, fn);
2308                 };
2309             } else {
2310                 return function() {
2311                 };
2312             }
2313         }(),
2314
2315
2316         doRemove: function() {
2317             if (window.removeEventListener) {
2318                 return function (el, eventName, fn, capture) {
2319                     el.removeEventListener(eventName, fn, (capture));
2320                 };
2321             } else if (window.detachEvent) {
2322                 return function (el, eventName, fn) {
2323                     el.detachEvent("on" + eventName, fn);
2324                 };
2325             } else {
2326                 return function() {
2327                 };
2328             }
2329         }()
2330     };
2331     
2332 }();
2333 (function() {     
2334    
2335     var E = Roo.lib.Event;
2336     E.on = E.addListener;
2337     E.un = E.removeListener;
2338
2339     if (document && document.body) {
2340         E._load();
2341     } else {
2342         E.doAdd(window, "load", E._load);
2343     }
2344     E.doAdd(window, "unload", E._unload);
2345     E._tryPreloadAttach();
2346 })();
2347
2348 /*
2349  * Portions of this file are based on pieces of Yahoo User Interface Library
2350  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2351  * YUI licensed under the BSD License:
2352  * http://developer.yahoo.net/yui/license.txt
2353  * <script type="text/javascript">
2354  *
2355  */
2356
2357 (function() {
2358     
2359     Roo.lib.Ajax = {
2360         request : function(method, uri, cb, data, options) {
2361             if(options){
2362                 var hs = options.headers;
2363                 if(hs){
2364                     for(var h in hs){
2365                         if(hs.hasOwnProperty(h)){
2366                             this.initHeader(h, hs[h], false);
2367                         }
2368                     }
2369                 }
2370                 if(options.xmlData){
2371                     this.initHeader('Content-Type', 'text/xml', false);
2372                     method = 'POST';
2373                     data = options.xmlData;
2374                 }
2375             }
2376
2377             return this.asyncRequest(method, uri, cb, data);
2378         },
2379
2380         serializeForm : function(form) {
2381             if(typeof form == 'string') {
2382                 form = (document.getElementById(form) || document.forms[form]);
2383             }
2384
2385             var el, name, val, disabled, data = '', hasSubmit = false;
2386             for (var i = 0; i < form.elements.length; i++) {
2387                 el = form.elements[i];
2388                 disabled = form.elements[i].disabled;
2389                 name = form.elements[i].name;
2390                 val = form.elements[i].value;
2391
2392                 if (!disabled && name){
2393                     switch (el.type)
2394                             {
2395                         case 'select-one':
2396                         case 'select-multiple':
2397                             for (var j = 0; j < el.options.length; j++) {
2398                                 if (el.options[j].selected) {
2399                                     if (Roo.isIE) {
2400                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2401                                     }
2402                                     else {
2403                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2404                                     }
2405                                 }
2406                             }
2407                             break;
2408                         case 'radio':
2409                         case 'checkbox':
2410                             if (el.checked) {
2411                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2412                             }
2413                             break;
2414                         case 'file':
2415
2416                         case undefined:
2417
2418                         case 'reset':
2419
2420                         case 'button':
2421
2422                             break;
2423                         case 'submit':
2424                             if(hasSubmit == false) {
2425                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2426                                 hasSubmit = true;
2427                             }
2428                             break;
2429                         default:
2430                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2431                             break;
2432                     }
2433                 }
2434             }
2435             data = data.substr(0, data.length - 1);
2436             return data;
2437         },
2438
2439         headers:{},
2440
2441         hasHeaders:false,
2442
2443         useDefaultHeader:true,
2444
2445         defaultPostHeader:'application/x-www-form-urlencoded',
2446
2447         useDefaultXhrHeader:true,
2448
2449         defaultXhrHeader:'XMLHttpRequest',
2450
2451         hasDefaultHeaders:true,
2452
2453         defaultHeaders:{},
2454
2455         poll:{},
2456
2457         timeout:{},
2458
2459         pollInterval:50,
2460
2461         transactionId:0,
2462
2463         setProgId:function(id)
2464         {
2465             this.activeX.unshift(id);
2466         },
2467
2468         setDefaultPostHeader:function(b)
2469         {
2470             this.useDefaultHeader = b;
2471         },
2472
2473         setDefaultXhrHeader:function(b)
2474         {
2475             this.useDefaultXhrHeader = b;
2476         },
2477
2478         setPollingInterval:function(i)
2479         {
2480             if (typeof i == 'number' && isFinite(i)) {
2481                 this.pollInterval = i;
2482             }
2483         },
2484
2485         createXhrObject:function(transactionId)
2486         {
2487             var obj,http;
2488             try
2489             {
2490
2491                 http = new XMLHttpRequest();
2492
2493                 obj = { conn:http, tId:transactionId };
2494             }
2495             catch(e)
2496             {
2497                 for (var i = 0; i < this.activeX.length; ++i) {
2498                     try
2499                     {
2500
2501                         http = new ActiveXObject(this.activeX[i]);
2502
2503                         obj = { conn:http, tId:transactionId };
2504                         break;
2505                     }
2506                     catch(e) {
2507                     }
2508                 }
2509             }
2510             finally
2511             {
2512                 return obj;
2513             }
2514         },
2515
2516         getConnectionObject:function()
2517         {
2518             var o;
2519             var tId = this.transactionId;
2520
2521             try
2522             {
2523                 o = this.createXhrObject(tId);
2524                 if (o) {
2525                     this.transactionId++;
2526                 }
2527             }
2528             catch(e) {
2529             }
2530             finally
2531             {
2532                 return o;
2533             }
2534         },
2535
2536         asyncRequest:function(method, uri, callback, postData)
2537         {
2538             var o = this.getConnectionObject();
2539
2540             if (!o) {
2541                 return null;
2542             }
2543             else {
2544                 o.conn.open(method, uri, true);
2545
2546                 if (this.useDefaultXhrHeader) {
2547                     if (!this.defaultHeaders['X-Requested-With']) {
2548                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2549                     }
2550                 }
2551
2552                 if(postData && this.useDefaultHeader){
2553                     this.initHeader('Content-Type', this.defaultPostHeader);
2554                 }
2555
2556                  if (this.hasDefaultHeaders || this.hasHeaders) {
2557                     this.setHeader(o);
2558                 }
2559
2560                 this.handleReadyState(o, callback);
2561                 o.conn.send(postData || null);
2562
2563                 return o;
2564             }
2565         },
2566
2567         handleReadyState:function(o, callback)
2568         {
2569             var oConn = this;
2570
2571             if (callback && callback.timeout) {
2572                 this.timeout[o.tId] = window.setTimeout(function() {
2573                     oConn.abort(o, callback, true);
2574                 }, callback.timeout);
2575             }
2576
2577             this.poll[o.tId] = window.setInterval(
2578                     function() {
2579                         if (o.conn && o.conn.readyState == 4) {
2580                             window.clearInterval(oConn.poll[o.tId]);
2581                             delete oConn.poll[o.tId];
2582
2583                             if(callback && callback.timeout) {
2584                                 window.clearTimeout(oConn.timeout[o.tId]);
2585                                 delete oConn.timeout[o.tId];
2586                             }
2587
2588                             oConn.handleTransactionResponse(o, callback);
2589                         }
2590                     }
2591                     , this.pollInterval);
2592         },
2593
2594         handleTransactionResponse:function(o, callback, isAbort)
2595         {
2596
2597             if (!callback) {
2598                 this.releaseObject(o);
2599                 return;
2600             }
2601
2602             var httpStatus, responseObject;
2603
2604             try
2605             {
2606                 if (o.conn.status !== undefined && o.conn.status != 0) {
2607                     httpStatus = o.conn.status;
2608                 }
2609                 else {
2610                     httpStatus = 13030;
2611                 }
2612             }
2613             catch(e) {
2614
2615
2616                 httpStatus = 13030;
2617             }
2618
2619             if (httpStatus >= 200 && httpStatus < 300) {
2620                 responseObject = this.createResponseObject(o, callback.argument);
2621                 if (callback.success) {
2622                     if (!callback.scope) {
2623                         callback.success(responseObject);
2624                     }
2625                     else {
2626
2627
2628                         callback.success.apply(callback.scope, [responseObject]);
2629                     }
2630                 }
2631             }
2632             else {
2633                 switch (httpStatus) {
2634
2635                     case 12002:
2636                     case 12029:
2637                     case 12030:
2638                     case 12031:
2639                     case 12152:
2640                     case 13030:
2641                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2642                         if (callback.failure) {
2643                             if (!callback.scope) {
2644                                 callback.failure(responseObject);
2645                             }
2646                             else {
2647                                 callback.failure.apply(callback.scope, [responseObject]);
2648                             }
2649                         }
2650                         break;
2651                     default:
2652                         responseObject = this.createResponseObject(o, callback.argument);
2653                         if (callback.failure) {
2654                             if (!callback.scope) {
2655                                 callback.failure(responseObject);
2656                             }
2657                             else {
2658                                 callback.failure.apply(callback.scope, [responseObject]);
2659                             }
2660                         }
2661                 }
2662             }
2663
2664             this.releaseObject(o);
2665             responseObject = null;
2666         },
2667
2668         createResponseObject:function(o, callbackArg)
2669         {
2670             var obj = {};
2671             var headerObj = {};
2672
2673             try
2674             {
2675                 var headerStr = o.conn.getAllResponseHeaders();
2676                 var header = headerStr.split('\n');
2677                 for (var i = 0; i < header.length; i++) {
2678                     var delimitPos = header[i].indexOf(':');
2679                     if (delimitPos != -1) {
2680                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2681                     }
2682                 }
2683             }
2684             catch(e) {
2685             }
2686
2687             obj.tId = o.tId;
2688             obj.status = o.conn.status;
2689             obj.statusText = o.conn.statusText;
2690             obj.getResponseHeader = headerObj;
2691             obj.getAllResponseHeaders = headerStr;
2692             obj.responseText = o.conn.responseText;
2693             obj.responseXML = o.conn.responseXML;
2694
2695             if (typeof callbackArg !== undefined) {
2696                 obj.argument = callbackArg;
2697             }
2698
2699             return obj;
2700         },
2701
2702         createExceptionObject:function(tId, callbackArg, isAbort)
2703         {
2704             var COMM_CODE = 0;
2705             var COMM_ERROR = 'communication failure';
2706             var ABORT_CODE = -1;
2707             var ABORT_ERROR = 'transaction aborted';
2708
2709             var obj = {};
2710
2711             obj.tId = tId;
2712             if (isAbort) {
2713                 obj.status = ABORT_CODE;
2714                 obj.statusText = ABORT_ERROR;
2715             }
2716             else {
2717                 obj.status = COMM_CODE;
2718                 obj.statusText = COMM_ERROR;
2719             }
2720
2721             if (callbackArg) {
2722                 obj.argument = callbackArg;
2723             }
2724
2725             return obj;
2726         },
2727
2728         initHeader:function(label, value, isDefault)
2729         {
2730             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2731
2732             if (headerObj[label] === undefined) {
2733                 headerObj[label] = value;
2734             }
2735             else {
2736
2737
2738                 headerObj[label] = value + "," + headerObj[label];
2739             }
2740
2741             if (isDefault) {
2742                 this.hasDefaultHeaders = true;
2743             }
2744             else {
2745                 this.hasHeaders = true;
2746             }
2747         },
2748
2749
2750         setHeader:function(o)
2751         {
2752             if (this.hasDefaultHeaders) {
2753                 for (var prop in this.defaultHeaders) {
2754                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2755                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2756                     }
2757                 }
2758             }
2759
2760             if (this.hasHeaders) {
2761                 for (var prop in this.headers) {
2762                     if (this.headers.hasOwnProperty(prop)) {
2763                         o.conn.setRequestHeader(prop, this.headers[prop]);
2764                     }
2765                 }
2766                 this.headers = {};
2767                 this.hasHeaders = false;
2768             }
2769         },
2770
2771         resetDefaultHeaders:function() {
2772             delete this.defaultHeaders;
2773             this.defaultHeaders = {};
2774             this.hasDefaultHeaders = false;
2775         },
2776
2777         abort:function(o, callback, isTimeout)
2778         {
2779             if(this.isCallInProgress(o)) {
2780                 o.conn.abort();
2781                 window.clearInterval(this.poll[o.tId]);
2782                 delete this.poll[o.tId];
2783                 if (isTimeout) {
2784                     delete this.timeout[o.tId];
2785                 }
2786
2787                 this.handleTransactionResponse(o, callback, true);
2788
2789                 return true;
2790             }
2791             else {
2792                 return false;
2793             }
2794         },
2795
2796
2797         isCallInProgress:function(o)
2798         {
2799             if (o && o.conn) {
2800                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2801             }
2802             else {
2803
2804                 return false;
2805             }
2806         },
2807
2808
2809         releaseObject:function(o)
2810         {
2811
2812             o.conn = null;
2813
2814             o = null;
2815         },
2816
2817         activeX:[
2818         'MSXML2.XMLHTTP.3.0',
2819         'MSXML2.XMLHTTP',
2820         'Microsoft.XMLHTTP'
2821         ]
2822
2823
2824     };
2825 })();/*
2826  * Portions of this file are based on pieces of Yahoo User Interface Library
2827  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2828  * YUI licensed under the BSD License:
2829  * http://developer.yahoo.net/yui/license.txt
2830  * <script type="text/javascript">
2831  *
2832  */
2833
2834 Roo.lib.Region = function(t, r, b, l) {
2835     this.top = t;
2836     this[1] = t;
2837     this.right = r;
2838     this.bottom = b;
2839     this.left = l;
2840     this[0] = l;
2841 };
2842
2843
2844 Roo.lib.Region.prototype = {
2845     contains : function(region) {
2846         return ( region.left >= this.left &&
2847                  region.right <= this.right &&
2848                  region.top >= this.top &&
2849                  region.bottom <= this.bottom    );
2850
2851     },
2852
2853     getArea : function() {
2854         return ( (this.bottom - this.top) * (this.right - this.left) );
2855     },
2856
2857     intersect : function(region) {
2858         var t = Math.max(this.top, region.top);
2859         var r = Math.min(this.right, region.right);
2860         var b = Math.min(this.bottom, region.bottom);
2861         var l = Math.max(this.left, region.left);
2862
2863         if (b >= t && r >= l) {
2864             return new Roo.lib.Region(t, r, b, l);
2865         } else {
2866             return null;
2867         }
2868     },
2869     union : function(region) {
2870         var t = Math.min(this.top, region.top);
2871         var r = Math.max(this.right, region.right);
2872         var b = Math.max(this.bottom, region.bottom);
2873         var l = Math.min(this.left, region.left);
2874
2875         return new Roo.lib.Region(t, r, b, l);
2876     },
2877
2878     adjust : function(t, l, b, r) {
2879         this.top += t;
2880         this.left += l;
2881         this.right += r;
2882         this.bottom += b;
2883         return this;
2884     }
2885 };
2886
2887 Roo.lib.Region.getRegion = function(el) {
2888     var p = Roo.lib.Dom.getXY(el);
2889
2890     var t = p[1];
2891     var r = p[0] + el.offsetWidth;
2892     var b = p[1] + el.offsetHeight;
2893     var l = p[0];
2894
2895     return new Roo.lib.Region(t, r, b, l);
2896 };
2897 /*
2898  * Portions of this file are based on pieces of Yahoo User Interface Library
2899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2900  * YUI licensed under the BSD License:
2901  * http://developer.yahoo.net/yui/license.txt
2902  * <script type="text/javascript">
2903  *
2904  */
2905 //@@dep Roo.lib.Region
2906
2907
2908 Roo.lib.Point = function(x, y) {
2909     if (x instanceof Array) {
2910         y = x[1];
2911         x = x[0];
2912     }
2913     this.x = this.right = this.left = this[0] = x;
2914     this.y = this.top = this.bottom = this[1] = y;
2915 };
2916
2917 Roo.lib.Point.prototype = new Roo.lib.Region();
2918 /*
2919  * Portions of this file are based on pieces of Yahoo User Interface Library
2920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2921  * YUI licensed under the BSD License:
2922  * http://developer.yahoo.net/yui/license.txt
2923  * <script type="text/javascript">
2924  *
2925  */
2926  
2927 (function() {   
2928
2929     Roo.lib.Anim = {
2930         scroll : function(el, args, duration, easing, cb, scope) {
2931             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2932         },
2933
2934         motion : function(el, args, duration, easing, cb, scope) {
2935             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2936         },
2937
2938         color : function(el, args, duration, easing, cb, scope) {
2939             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2940         },
2941
2942         run : function(el, args, duration, easing, cb, scope, type) {
2943             type = type || Roo.lib.AnimBase;
2944             if (typeof easing == "string") {
2945                 easing = Roo.lib.Easing[easing];
2946             }
2947             var anim = new type(el, args, duration, easing);
2948             anim.animateX(function() {
2949                 Roo.callback(cb, scope);
2950             });
2951             return anim;
2952         }
2953     };
2954 })();/*
2955  * Portions of this file are based on pieces of Yahoo User Interface Library
2956  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2957  * YUI licensed under the BSD License:
2958  * http://developer.yahoo.net/yui/license.txt
2959  * <script type="text/javascript">
2960  *
2961  */
2962
2963 (function() {    
2964     var libFlyweight;
2965     
2966     function fly(el) {
2967         if (!libFlyweight) {
2968             libFlyweight = new Roo.Element.Flyweight();
2969         }
2970         libFlyweight.dom = el;
2971         return libFlyweight;
2972     }
2973
2974     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2975     
2976    
2977     
2978     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2979         if (el) {
2980             this.init(el, attributes, duration, method);
2981         }
2982     };
2983
2984     Roo.lib.AnimBase.fly = fly;
2985     
2986     
2987     
2988     Roo.lib.AnimBase.prototype = {
2989
2990         toString: function() {
2991             var el = this.getEl();
2992             var id = el.id || el.tagName;
2993             return ("Anim " + id);
2994         },
2995
2996         patterns: {
2997             noNegatives:        /width|height|opacity|padding/i,
2998             offsetAttribute:  /^((width|height)|(top|left))$/,
2999             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3000             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3001         },
3002
3003
3004         doMethod: function(attr, start, end) {
3005             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3006         },
3007
3008
3009         setAttribute: function(attr, val, unit) {
3010             if (this.patterns.noNegatives.test(attr)) {
3011                 val = (val > 0) ? val : 0;
3012             }
3013
3014             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3015         },
3016
3017
3018         getAttribute: function(attr) {
3019             var el = this.getEl();
3020             var val = fly(el).getStyle(attr);
3021
3022             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3023                 return parseFloat(val);
3024             }
3025
3026             var a = this.patterns.offsetAttribute.exec(attr) || [];
3027             var pos = !!( a[3] );
3028             var box = !!( a[2] );
3029
3030
3031             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3032                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3033             } else {
3034                 val = 0;
3035             }
3036
3037             return val;
3038         },
3039
3040
3041         getDefaultUnit: function(attr) {
3042             if (this.patterns.defaultUnit.test(attr)) {
3043                 return 'px';
3044             }
3045
3046             return '';
3047         },
3048
3049         animateX : function(callback, scope) {
3050             var f = function() {
3051                 this.onComplete.removeListener(f);
3052                 if (typeof callback == "function") {
3053                     callback.call(scope || this, this);
3054                 }
3055             };
3056             this.onComplete.addListener(f, this);
3057             this.animate();
3058         },
3059
3060
3061         setRuntimeAttribute: function(attr) {
3062             var start;
3063             var end;
3064             var attributes = this.attributes;
3065
3066             this.runtimeAttributes[attr] = {};
3067
3068             var isset = function(prop) {
3069                 return (typeof prop !== 'undefined');
3070             };
3071
3072             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3073                 return false;
3074             }
3075
3076             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3077
3078
3079             if (isset(attributes[attr]['to'])) {
3080                 end = attributes[attr]['to'];
3081             } else if (isset(attributes[attr]['by'])) {
3082                 if (start.constructor == Array) {
3083                     end = [];
3084                     for (var i = 0, len = start.length; i < len; ++i) {
3085                         end[i] = start[i] + attributes[attr]['by'][i];
3086                     }
3087                 } else {
3088                     end = start + attributes[attr]['by'];
3089                 }
3090             }
3091
3092             this.runtimeAttributes[attr].start = start;
3093             this.runtimeAttributes[attr].end = end;
3094
3095
3096             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3097         },
3098
3099
3100         init: function(el, attributes, duration, method) {
3101
3102             var isAnimated = false;
3103
3104
3105             var startTime = null;
3106
3107
3108             var actualFrames = 0;
3109
3110
3111             el = Roo.getDom(el);
3112
3113
3114             this.attributes = attributes || {};
3115
3116
3117             this.duration = duration || 1;
3118
3119
3120             this.method = method || Roo.lib.Easing.easeNone;
3121
3122
3123             this.useSeconds = true;
3124
3125
3126             this.currentFrame = 0;
3127
3128
3129             this.totalFrames = Roo.lib.AnimMgr.fps;
3130
3131
3132             this.getEl = function() {
3133                 return el;
3134             };
3135
3136
3137             this.isAnimated = function() {
3138                 return isAnimated;
3139             };
3140
3141
3142             this.getStartTime = function() {
3143                 return startTime;
3144             };
3145
3146             this.runtimeAttributes = {};
3147
3148
3149             this.animate = function() {
3150                 if (this.isAnimated()) {
3151                     return false;
3152                 }
3153
3154                 this.currentFrame = 0;
3155
3156                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3157
3158                 Roo.lib.AnimMgr.registerElement(this);
3159             };
3160
3161
3162             this.stop = function(finish) {
3163                 if (finish) {
3164                     this.currentFrame = this.totalFrames;
3165                     this._onTween.fire();
3166                 }
3167                 Roo.lib.AnimMgr.stop(this);
3168             };
3169
3170             var onStart = function() {
3171                 this.onStart.fire();
3172
3173                 this.runtimeAttributes = {};
3174                 for (var attr in this.attributes) {
3175                     this.setRuntimeAttribute(attr);
3176                 }
3177
3178                 isAnimated = true;
3179                 actualFrames = 0;
3180                 startTime = new Date();
3181             };
3182
3183
3184             var onTween = function() {
3185                 var data = {
3186                     duration: new Date() - this.getStartTime(),
3187                     currentFrame: this.currentFrame
3188                 };
3189
3190                 data.toString = function() {
3191                     return (
3192                             'duration: ' + data.duration +
3193                             ', currentFrame: ' + data.currentFrame
3194                             );
3195                 };
3196
3197                 this.onTween.fire(data);
3198
3199                 var runtimeAttributes = this.runtimeAttributes;
3200
3201                 for (var attr in runtimeAttributes) {
3202                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3203                 }
3204
3205                 actualFrames += 1;
3206             };
3207
3208             var onComplete = function() {
3209                 var actual_duration = (new Date() - startTime) / 1000 ;
3210
3211                 var data = {
3212                     duration: actual_duration,
3213                     frames: actualFrames,
3214                     fps: actualFrames / actual_duration
3215                 };
3216
3217                 data.toString = function() {
3218                     return (
3219                             'duration: ' + data.duration +
3220                             ', frames: ' + data.frames +
3221                             ', fps: ' + data.fps
3222                             );
3223                 };
3224
3225                 isAnimated = false;
3226                 actualFrames = 0;
3227                 this.onComplete.fire(data);
3228             };
3229
3230
3231             this._onStart = new Roo.util.Event(this);
3232             this.onStart = new Roo.util.Event(this);
3233             this.onTween = new Roo.util.Event(this);
3234             this._onTween = new Roo.util.Event(this);
3235             this.onComplete = new Roo.util.Event(this);
3236             this._onComplete = new Roo.util.Event(this);
3237             this._onStart.addListener(onStart);
3238             this._onTween.addListener(onTween);
3239             this._onComplete.addListener(onComplete);
3240         }
3241     };
3242 })();
3243 /*
3244  * Portions of this file are based on pieces of Yahoo User Interface Library
3245  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3246  * YUI licensed under the BSD License:
3247  * http://developer.yahoo.net/yui/license.txt
3248  * <script type="text/javascript">
3249  *
3250  */
3251
3252 Roo.lib.AnimMgr = new function() {
3253
3254         var thread = null;
3255
3256
3257         var queue = [];
3258
3259
3260         var tweenCount = 0;
3261
3262
3263         this.fps = 1000;
3264
3265
3266         this.delay = 1;
3267
3268
3269         this.registerElement = function(tween) {
3270             queue[queue.length] = tween;
3271             tweenCount += 1;
3272             tween._onStart.fire();
3273             this.start();
3274         };
3275
3276
3277         this.unRegister = function(tween, index) {
3278             tween._onComplete.fire();
3279             index = index || getIndex(tween);
3280             if (index != -1) {
3281                 queue.splice(index, 1);
3282             }
3283
3284             tweenCount -= 1;
3285             if (tweenCount <= 0) {
3286                 this.stop();
3287             }
3288         };
3289
3290
3291         this.start = function() {
3292             if (thread === null) {
3293                 thread = setInterval(this.run, this.delay);
3294             }
3295         };
3296
3297
3298         this.stop = function(tween) {
3299             if (!tween) {
3300                 clearInterval(thread);
3301
3302                 for (var i = 0, len = queue.length; i < len; ++i) {
3303                     if (queue[0].isAnimated()) {
3304                         this.unRegister(queue[0], 0);
3305                     }
3306                 }
3307
3308                 queue = [];
3309                 thread = null;
3310                 tweenCount = 0;
3311             }
3312             else {
3313                 this.unRegister(tween);
3314             }
3315         };
3316
3317
3318         this.run = function() {
3319             for (var i = 0, len = queue.length; i < len; ++i) {
3320                 var tween = queue[i];
3321                 if (!tween || !tween.isAnimated()) {
3322                     continue;
3323                 }
3324
3325                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3326                 {
3327                     tween.currentFrame += 1;
3328
3329                     if (tween.useSeconds) {
3330                         correctFrame(tween);
3331                     }
3332                     tween._onTween.fire();
3333                 }
3334                 else {
3335                     Roo.lib.AnimMgr.stop(tween, i);
3336                 }
3337             }
3338         };
3339
3340         var getIndex = function(anim) {
3341             for (var i = 0, len = queue.length; i < len; ++i) {
3342                 if (queue[i] == anim) {
3343                     return i;
3344                 }
3345             }
3346             return -1;
3347         };
3348
3349
3350         var correctFrame = function(tween) {
3351             var frames = tween.totalFrames;
3352             var frame = tween.currentFrame;
3353             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3354             var elapsed = (new Date() - tween.getStartTime());
3355             var tweak = 0;
3356
3357             if (elapsed < tween.duration * 1000) {
3358                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3359             } else {
3360                 tweak = frames - (frame + 1);
3361             }
3362             if (tweak > 0 && isFinite(tweak)) {
3363                 if (tween.currentFrame + tweak >= frames) {
3364                     tweak = frames - (frame + 1);
3365                 }
3366
3367                 tween.currentFrame += tweak;
3368             }
3369         };
3370     };/*
3371  * Portions of this file are based on pieces of Yahoo User Interface Library
3372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3373  * YUI licensed under the BSD License:
3374  * http://developer.yahoo.net/yui/license.txt
3375  * <script type="text/javascript">
3376  *
3377  */
3378 Roo.lib.Bezier = new function() {
3379
3380         this.getPosition = function(points, t) {
3381             var n = points.length;
3382             var tmp = [];
3383
3384             for (var i = 0; i < n; ++i) {
3385                 tmp[i] = [points[i][0], points[i][1]];
3386             }
3387
3388             for (var j = 1; j < n; ++j) {
3389                 for (i = 0; i < n - j; ++i) {
3390                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3391                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3392                 }
3393             }
3394
3395             return [ tmp[0][0], tmp[0][1] ];
3396
3397         };
3398     };/*
3399  * Portions of this file are based on pieces of Yahoo User Interface Library
3400  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3401  * YUI licensed under the BSD License:
3402  * http://developer.yahoo.net/yui/license.txt
3403  * <script type="text/javascript">
3404  *
3405  */
3406 (function() {
3407
3408     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3409         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3410     };
3411
3412     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3413
3414     var fly = Roo.lib.AnimBase.fly;
3415     var Y = Roo.lib;
3416     var superclass = Y.ColorAnim.superclass;
3417     var proto = Y.ColorAnim.prototype;
3418
3419     proto.toString = function() {
3420         var el = this.getEl();
3421         var id = el.id || el.tagName;
3422         return ("ColorAnim " + id);
3423     };
3424
3425     proto.patterns.color = /color$/i;
3426     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3427     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3428     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3429     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3430
3431
3432     proto.parseColor = function(s) {
3433         if (s.length == 3) {
3434             return s;
3435         }
3436
3437         var c = this.patterns.hex.exec(s);
3438         if (c && c.length == 4) {
3439             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3440         }
3441
3442         c = this.patterns.rgb.exec(s);
3443         if (c && c.length == 4) {
3444             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3445         }
3446
3447         c = this.patterns.hex3.exec(s);
3448         if (c && c.length == 4) {
3449             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3450         }
3451
3452         return null;
3453     };
3454     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3455     proto.getAttribute = function(attr) {
3456         var el = this.getEl();
3457         if (this.patterns.color.test(attr)) {
3458             var val = fly(el).getStyle(attr);
3459
3460             if (this.patterns.transparent.test(val)) {
3461                 var parent = el.parentNode;
3462                 val = fly(parent).getStyle(attr);
3463
3464                 while (parent && this.patterns.transparent.test(val)) {
3465                     parent = parent.parentNode;
3466                     val = fly(parent).getStyle(attr);
3467                     if (parent.tagName.toUpperCase() == 'HTML') {
3468                         val = '#fff';
3469                     }
3470                 }
3471             }
3472         } else {
3473             val = superclass.getAttribute.call(this, attr);
3474         }
3475
3476         return val;
3477     };
3478     proto.getAttribute = function(attr) {
3479         var el = this.getEl();
3480         if (this.patterns.color.test(attr)) {
3481             var val = fly(el).getStyle(attr);
3482
3483             if (this.patterns.transparent.test(val)) {
3484                 var parent = el.parentNode;
3485                 val = fly(parent).getStyle(attr);
3486
3487                 while (parent && this.patterns.transparent.test(val)) {
3488                     parent = parent.parentNode;
3489                     val = fly(parent).getStyle(attr);
3490                     if (parent.tagName.toUpperCase() == 'HTML') {
3491                         val = '#fff';
3492                     }
3493                 }
3494             }
3495         } else {
3496             val = superclass.getAttribute.call(this, attr);
3497         }
3498
3499         return val;
3500     };
3501
3502     proto.doMethod = function(attr, start, end) {
3503         var val;
3504
3505         if (this.patterns.color.test(attr)) {
3506             val = [];
3507             for (var i = 0, len = start.length; i < len; ++i) {
3508                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3509             }
3510
3511             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3512         }
3513         else {
3514             val = superclass.doMethod.call(this, attr, start, end);
3515         }
3516
3517         return val;
3518     };
3519
3520     proto.setRuntimeAttribute = function(attr) {
3521         superclass.setRuntimeAttribute.call(this, attr);
3522
3523         if (this.patterns.color.test(attr)) {
3524             var attributes = this.attributes;
3525             var start = this.parseColor(this.runtimeAttributes[attr].start);
3526             var end = this.parseColor(this.runtimeAttributes[attr].end);
3527
3528             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3529                 end = this.parseColor(attributes[attr].by);
3530
3531                 for (var i = 0, len = start.length; i < len; ++i) {
3532                     end[i] = start[i] + end[i];
3533                 }
3534             }
3535
3536             this.runtimeAttributes[attr].start = start;
3537             this.runtimeAttributes[attr].end = end;
3538         }
3539     };
3540 })();
3541
3542 /*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 Roo.lib.Easing = {
3551
3552
3553     easeNone: function (t, b, c, d) {
3554         return c * t / d + b;
3555     },
3556
3557
3558     easeIn: function (t, b, c, d) {
3559         return c * (t /= d) * t + b;
3560     },
3561
3562
3563     easeOut: function (t, b, c, d) {
3564         return -c * (t /= d) * (t - 2) + b;
3565     },
3566
3567
3568     easeBoth: function (t, b, c, d) {
3569         if ((t /= d / 2) < 1) {
3570             return c / 2 * t * t + b;
3571         }
3572
3573         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3574     },
3575
3576
3577     easeInStrong: function (t, b, c, d) {
3578         return c * (t /= d) * t * t * t + b;
3579     },
3580
3581
3582     easeOutStrong: function (t, b, c, d) {
3583         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3584     },
3585
3586
3587     easeBothStrong: function (t, b, c, d) {
3588         if ((t /= d / 2) < 1) {
3589             return c / 2 * t * t * t * t + b;
3590         }
3591
3592         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3593     },
3594
3595
3596
3597     elasticIn: function (t, b, c, d, a, p) {
3598         if (t == 0) {
3599             return b;
3600         }
3601         if ((t /= d) == 1) {
3602             return b + c;
3603         }
3604         if (!p) {
3605             p = d * .3;
3606         }
3607
3608         if (!a || a < Math.abs(c)) {
3609             a = c;
3610             var s = p / 4;
3611         }
3612         else {
3613             var s = p / (2 * Math.PI) * Math.asin(c / a);
3614         }
3615
3616         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3617     },
3618
3619
3620     elasticOut: function (t, b, c, d, a, p) {
3621         if (t == 0) {
3622             return b;
3623         }
3624         if ((t /= d) == 1) {
3625             return b + c;
3626         }
3627         if (!p) {
3628             p = d * .3;
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         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3640     },
3641
3642
3643     elasticBoth: function (t, b, c, d, a, p) {
3644         if (t == 0) {
3645             return b;
3646         }
3647
3648         if ((t /= d / 2) == 2) {
3649             return b + c;
3650         }
3651
3652         if (!p) {
3653             p = d * (.3 * 1.5);
3654         }
3655
3656         if (!a || a < Math.abs(c)) {
3657             a = c;
3658             var s = p / 4;
3659         }
3660         else {
3661             var s = p / (2 * Math.PI) * Math.asin(c / a);
3662         }
3663
3664         if (t < 1) {
3665             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3666                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3667         }
3668         return a * Math.pow(2, -10 * (t -= 1)) *
3669                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3670     },
3671
3672
3673
3674     backIn: function (t, b, c, d, s) {
3675         if (typeof s == 'undefined') {
3676             s = 1.70158;
3677         }
3678         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3679     },
3680
3681
3682     backOut: function (t, b, c, d, s) {
3683         if (typeof s == 'undefined') {
3684             s = 1.70158;
3685         }
3686         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3687     },
3688
3689
3690     backBoth: function (t, b, c, d, s) {
3691         if (typeof s == 'undefined') {
3692             s = 1.70158;
3693         }
3694
3695         if ((t /= d / 2 ) < 1) {
3696             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3697         }
3698         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3699     },
3700
3701
3702     bounceIn: function (t, b, c, d) {
3703         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3704     },
3705
3706
3707     bounceOut: function (t, b, c, d) {
3708         if ((t /= d) < (1 / 2.75)) {
3709             return c * (7.5625 * t * t) + b;
3710         } else if (t < (2 / 2.75)) {
3711             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3712         } else if (t < (2.5 / 2.75)) {
3713             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3714         }
3715         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3716     },
3717
3718
3719     bounceBoth: function (t, b, c, d) {
3720         if (t < d / 2) {
3721             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3722         }
3723         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3724     }
3725 };/*
3726  * Portions of this file are based on pieces of Yahoo User Interface Library
3727  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3728  * YUI licensed under the BSD License:
3729  * http://developer.yahoo.net/yui/license.txt
3730  * <script type="text/javascript">
3731  *
3732  */
3733     (function() {
3734         Roo.lib.Motion = function(el, attributes, duration, method) {
3735             if (el) {
3736                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3737             }
3738         };
3739
3740         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3741
3742
3743         var Y = Roo.lib;
3744         var superclass = Y.Motion.superclass;
3745         var proto = Y.Motion.prototype;
3746
3747         proto.toString = function() {
3748             var el = this.getEl();
3749             var id = el.id || el.tagName;
3750             return ("Motion " + id);
3751         };
3752
3753         proto.patterns.points = /^points$/i;
3754
3755         proto.setAttribute = function(attr, val, unit) {
3756             if (this.patterns.points.test(attr)) {
3757                 unit = unit || 'px';
3758                 superclass.setAttribute.call(this, 'left', val[0], unit);
3759                 superclass.setAttribute.call(this, 'top', val[1], unit);
3760             } else {
3761                 superclass.setAttribute.call(this, attr, val, unit);
3762             }
3763         };
3764
3765         proto.getAttribute = function(attr) {
3766             if (this.patterns.points.test(attr)) {
3767                 var val = [
3768                         superclass.getAttribute.call(this, 'left'),
3769                         superclass.getAttribute.call(this, 'top')
3770                         ];
3771             } else {
3772                 val = superclass.getAttribute.call(this, attr);
3773             }
3774
3775             return val;
3776         };
3777
3778         proto.doMethod = function(attr, start, end) {
3779             var val = null;
3780
3781             if (this.patterns.points.test(attr)) {
3782                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3783                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3784             } else {
3785                 val = superclass.doMethod.call(this, attr, start, end);
3786             }
3787             return val;
3788         };
3789
3790         proto.setRuntimeAttribute = function(attr) {
3791             if (this.patterns.points.test(attr)) {
3792                 var el = this.getEl();
3793                 var attributes = this.attributes;
3794                 var start;
3795                 var control = attributes['points']['control'] || [];
3796                 var end;
3797                 var i, len;
3798
3799                 if (control.length > 0 && !(control[0] instanceof Array)) {
3800                     control = [control];
3801                 } else {
3802                     var tmp = [];
3803                     for (i = 0,len = control.length; i < len; ++i) {
3804                         tmp[i] = control[i];
3805                     }
3806                     control = tmp;
3807                 }
3808
3809                 Roo.fly(el).position();
3810
3811                 if (isset(attributes['points']['from'])) {
3812                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3813                 }
3814                 else {
3815                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3816                 }
3817
3818                 start = this.getAttribute('points');
3819
3820
3821                 if (isset(attributes['points']['to'])) {
3822                     end = translateValues.call(this, attributes['points']['to'], start);
3823
3824                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3825                     for (i = 0,len = control.length; i < len; ++i) {
3826                         control[i] = translateValues.call(this, control[i], start);
3827                     }
3828
3829
3830                 } else if (isset(attributes['points']['by'])) {
3831                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3832
3833                     for (i = 0,len = control.length; i < len; ++i) {
3834                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3835                     }
3836                 }
3837
3838                 this.runtimeAttributes[attr] = [start];
3839
3840                 if (control.length > 0) {
3841                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3842                 }
3843
3844                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3845             }
3846             else {
3847                 superclass.setRuntimeAttribute.call(this, attr);
3848             }
3849         };
3850
3851         var translateValues = function(val, start) {
3852             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3853             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3854
3855             return val;
3856         };
3857
3858         var isset = function(prop) {
3859             return (typeof prop !== 'undefined');
3860         };
3861     })();
3862 /*
3863  * Portions of this file are based on pieces of Yahoo User Interface Library
3864  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3865  * YUI licensed under the BSD License:
3866  * http://developer.yahoo.net/yui/license.txt
3867  * <script type="text/javascript">
3868  *
3869  */
3870     (function() {
3871         Roo.lib.Scroll = function(el, attributes, duration, method) {
3872             if (el) {
3873                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3874             }
3875         };
3876
3877         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3878
3879
3880         var Y = Roo.lib;
3881         var superclass = Y.Scroll.superclass;
3882         var proto = Y.Scroll.prototype;
3883
3884         proto.toString = function() {
3885             var el = this.getEl();
3886             var id = el.id || el.tagName;
3887             return ("Scroll " + id);
3888         };
3889
3890         proto.doMethod = function(attr, start, end) {
3891             var val = null;
3892
3893             if (attr == 'scroll') {
3894                 val = [
3895                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3896                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3897                         ];
3898
3899             } else {
3900                 val = superclass.doMethod.call(this, attr, start, end);
3901             }
3902             return val;
3903         };
3904
3905         proto.getAttribute = function(attr) {
3906             var val = null;
3907             var el = this.getEl();
3908
3909             if (attr == 'scroll') {
3910                 val = [ el.scrollLeft, el.scrollTop ];
3911             } else {
3912                 val = superclass.getAttribute.call(this, attr);
3913             }
3914
3915             return val;
3916         };
3917
3918         proto.setAttribute = function(attr, val, unit) {
3919             var el = this.getEl();
3920
3921             if (attr == 'scroll') {
3922                 el.scrollLeft = val[0];
3923                 el.scrollTop = val[1];
3924             } else {
3925                 superclass.setAttribute.call(this, attr, val, unit);
3926             }
3927         };
3928     })();
3929 /*
3930  * Based on:
3931  * Ext JS Library 1.1.1
3932  * Copyright(c) 2006-2007, Ext JS, LLC.
3933  *
3934  * Originally Released Under LGPL - original licence link has changed is not relivant.
3935  *
3936  * Fork - LGPL
3937  * <script type="text/javascript">
3938  */
3939  
3940
3941 /**
3942  * @class Roo.DomHelper
3943  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3944  * 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>.
3945  * @singleton
3946  */
3947 Roo.DomHelper = function(){
3948     var tempTableEl = null;
3949     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3950     var tableRe = /^table|tbody|tr|td$/i;
3951     var xmlns = {};
3952     // build as innerHTML where available
3953     /** @ignore */
3954     var createHtml = function(o){
3955         if(typeof o == 'string'){
3956             return o;
3957         }
3958         var b = "";
3959         if(!o.tag){
3960             o.tag = "div";
3961         }
3962         b += "<" + o.tag;
3963         for(var attr in o){
3964             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3965             if(attr == "style"){
3966                 var s = o["style"];
3967                 if(typeof s == "function"){
3968                     s = s.call();
3969                 }
3970                 if(typeof s == "string"){
3971                     b += ' style="' + s + '"';
3972                 }else if(typeof s == "object"){
3973                     b += ' style="';
3974                     for(var key in s){
3975                         if(typeof s[key] != "function"){
3976                             b += key + ":" + s[key] + ";";
3977                         }
3978                     }
3979                     b += '"';
3980                 }
3981             }else{
3982                 if(attr == "cls"){
3983                     b += ' class="' + o["cls"] + '"';
3984                 }else if(attr == "htmlFor"){
3985                     b += ' for="' + o["htmlFor"] + '"';
3986                 }else{
3987                     b += " " + attr + '="' + o[attr] + '"';
3988                 }
3989             }
3990         }
3991         if(emptyTags.test(o.tag)){
3992             b += "/>";
3993         }else{
3994             b += ">";
3995             var cn = o.children || o.cn;
3996             if(cn){
3997                 //http://bugs.kde.org/show_bug.cgi?id=71506
3998                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3999                     for(var i = 0, len = cn.length; i < len; i++) {
4000                         b += createHtml(cn[i], b);
4001                     }
4002                 }else{
4003                     b += createHtml(cn, b);
4004                 }
4005             }
4006             if(o.html){
4007                 b += o.html;
4008             }
4009             b += "</" + o.tag + ">";
4010         }
4011         return b;
4012     };
4013
4014     // build as dom
4015     /** @ignore */
4016     var createDom = function(o, parentNode){
4017          
4018         // defininition craeted..
4019         var ns = false;
4020         if (o.ns && o.ns != 'html') {
4021                
4022             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4023                 xmlns[o.ns] = o.xmlns;
4024                 ns = o.xmlns;
4025             }
4026             if (typeof(xmlns[o.ns]) == 'undefined') {
4027                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4028             }
4029             ns = xmlns[o.ns];
4030         }
4031         
4032         
4033         if (typeof(o) == 'string') {
4034             return parentNode.appendChild(document.createTextNode(o));
4035         }
4036         o.tag = o.tag || div;
4037         if (o.ns && Roo.isIE) {
4038             ns = false;
4039             o.tag = o.ns + ':' + o.tag;
4040             
4041         }
4042         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4043         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4044         for(var attr in o){
4045             
4046             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4047                     attr == "style" || typeof o[attr] == "function") continue;
4048                     
4049             if(attr=="cls" && Roo.isIE){
4050                 el.className = o["cls"];
4051             }else{
4052                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4053                 else el[attr] = o[attr];
4054             }
4055         }
4056         Roo.DomHelper.applyStyles(el, o.style);
4057         var cn = o.children || o.cn;
4058         if(cn){
4059             //http://bugs.kde.org/show_bug.cgi?id=71506
4060              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4061                 for(var i = 0, len = cn.length; i < len; i++) {
4062                     createDom(cn[i], el);
4063                 }
4064             }else{
4065                 createDom(cn, el);
4066             }
4067         }
4068         if(o.html){
4069             el.innerHTML = o.html;
4070         }
4071         if(parentNode){
4072            parentNode.appendChild(el);
4073         }
4074         return el;
4075     };
4076
4077     var ieTable = function(depth, s, h, e){
4078         tempTableEl.innerHTML = [s, h, e].join('');
4079         var i = -1, el = tempTableEl;
4080         while(++i < depth){
4081             el = el.firstChild;
4082         }
4083         return el;
4084     };
4085
4086     // kill repeat to save bytes
4087     var ts = '<table>',
4088         te = '</table>',
4089         tbs = ts+'<tbody>',
4090         tbe = '</tbody>'+te,
4091         trs = tbs + '<tr>',
4092         tre = '</tr>'+tbe;
4093
4094     /**
4095      * @ignore
4096      * Nasty code for IE's broken table implementation
4097      */
4098     var insertIntoTable = function(tag, where, el, html){
4099         if(!tempTableEl){
4100             tempTableEl = document.createElement('div');
4101         }
4102         var node;
4103         var before = null;
4104         if(tag == 'td'){
4105             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4106                 return;
4107             }
4108             if(where == 'beforebegin'){
4109                 before = el;
4110                 el = el.parentNode;
4111             } else{
4112                 before = el.nextSibling;
4113                 el = el.parentNode;
4114             }
4115             node = ieTable(4, trs, html, tre);
4116         }
4117         else if(tag == 'tr'){
4118             if(where == 'beforebegin'){
4119                 before = el;
4120                 el = el.parentNode;
4121                 node = ieTable(3, tbs, html, tbe);
4122             } else if(where == 'afterend'){
4123                 before = el.nextSibling;
4124                 el = el.parentNode;
4125                 node = ieTable(3, tbs, html, tbe);
4126             } else{ // INTO a TR
4127                 if(where == 'afterbegin'){
4128                     before = el.firstChild;
4129                 }
4130                 node = ieTable(4, trs, html, tre);
4131             }
4132         } else if(tag == 'tbody'){
4133             if(where == 'beforebegin'){
4134                 before = el;
4135                 el = el.parentNode;
4136                 node = ieTable(2, ts, html, te);
4137             } else if(where == 'afterend'){
4138                 before = el.nextSibling;
4139                 el = el.parentNode;
4140                 node = ieTable(2, ts, html, te);
4141             } else{
4142                 if(where == 'afterbegin'){
4143                     before = el.firstChild;
4144                 }
4145                 node = ieTable(3, tbs, html, tbe);
4146             }
4147         } else{ // TABLE
4148             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4149                 return;
4150             }
4151             if(where == 'afterbegin'){
4152                 before = el.firstChild;
4153             }
4154             node = ieTable(2, ts, html, te);
4155         }
4156         el.insertBefore(node, before);
4157         return node;
4158     };
4159
4160     return {
4161     /** True to force the use of DOM instead of html fragments @type Boolean */
4162     useDom : false,
4163
4164     /**
4165      * Returns the markup for the passed Element(s) config
4166      * @param {Object} o The Dom object spec (and children)
4167      * @return {String}
4168      */
4169     markup : function(o){
4170         return createHtml(o);
4171     },
4172
4173     /**
4174      * Applies a style specification to an element
4175      * @param {String/HTMLElement} el The element to apply styles to
4176      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4177      * a function which returns such a specification.
4178      */
4179     applyStyles : function(el, styles){
4180         if(styles){
4181            el = Roo.fly(el);
4182            if(typeof styles == "string"){
4183                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4184                var matches;
4185                while ((matches = re.exec(styles)) != null){
4186                    el.setStyle(matches[1], matches[2]);
4187                }
4188            }else if (typeof styles == "object"){
4189                for (var style in styles){
4190                   el.setStyle(style, styles[style]);
4191                }
4192            }else if (typeof styles == "function"){
4193                 Roo.DomHelper.applyStyles(el, styles.call());
4194            }
4195         }
4196     },
4197
4198     /**
4199      * Inserts an HTML fragment into the Dom
4200      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4201      * @param {HTMLElement} el The context element
4202      * @param {String} html The HTML fragmenet
4203      * @return {HTMLElement} The new node
4204      */
4205     insertHtml : function(where, el, html){
4206         where = where.toLowerCase();
4207         if(el.insertAdjacentHTML){
4208             if(tableRe.test(el.tagName)){
4209                 var rs;
4210                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4211                     return rs;
4212                 }
4213             }
4214             switch(where){
4215                 case "beforebegin":
4216                     el.insertAdjacentHTML('BeforeBegin', html);
4217                     return el.previousSibling;
4218                 case "afterbegin":
4219                     el.insertAdjacentHTML('AfterBegin', html);
4220                     return el.firstChild;
4221                 case "beforeend":
4222                     el.insertAdjacentHTML('BeforeEnd', html);
4223                     return el.lastChild;
4224                 case "afterend":
4225                     el.insertAdjacentHTML('AfterEnd', html);
4226                     return el.nextSibling;
4227             }
4228             throw 'Illegal insertion point -> "' + where + '"';
4229         }
4230         var range = el.ownerDocument.createRange();
4231         var frag;
4232         switch(where){
4233              case "beforebegin":
4234                 range.setStartBefore(el);
4235                 frag = range.createContextualFragment(html);
4236                 el.parentNode.insertBefore(frag, el);
4237                 return el.previousSibling;
4238              case "afterbegin":
4239                 if(el.firstChild){
4240                     range.setStartBefore(el.firstChild);
4241                     frag = range.createContextualFragment(html);
4242                     el.insertBefore(frag, el.firstChild);
4243                     return el.firstChild;
4244                 }else{
4245                     el.innerHTML = html;
4246                     return el.firstChild;
4247                 }
4248             case "beforeend":
4249                 if(el.lastChild){
4250                     range.setStartAfter(el.lastChild);
4251                     frag = range.createContextualFragment(html);
4252                     el.appendChild(frag);
4253                     return el.lastChild;
4254                 }else{
4255                     el.innerHTML = html;
4256                     return el.lastChild;
4257                 }
4258             case "afterend":
4259                 range.setStartAfter(el);
4260                 frag = range.createContextualFragment(html);
4261                 el.parentNode.insertBefore(frag, el.nextSibling);
4262                 return el.nextSibling;
4263             }
4264             throw 'Illegal insertion point -> "' + where + '"';
4265     },
4266
4267     /**
4268      * Creates new Dom element(s) and inserts them before el
4269      * @param {String/HTMLElement/Element} el The context element
4270      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4271      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4272      * @return {HTMLElement/Roo.Element} The new node
4273      */
4274     insertBefore : function(el, o, returnElement){
4275         return this.doInsert(el, o, returnElement, "beforeBegin");
4276     },
4277
4278     /**
4279      * Creates new Dom element(s) and inserts them after el
4280      * @param {String/HTMLElement/Element} el The context element
4281      * @param {Object} o The Dom object spec (and children)
4282      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4283      * @return {HTMLElement/Roo.Element} The new node
4284      */
4285     insertAfter : function(el, o, returnElement){
4286         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4287     },
4288
4289     /**
4290      * Creates new Dom element(s) and inserts them as the first child of 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     insertFirst : function(el, o, returnElement){
4297         return this.doInsert(el, o, returnElement, "afterBegin");
4298     },
4299
4300     // private
4301     doInsert : function(el, o, returnElement, pos, sibling){
4302         el = Roo.getDom(el);
4303         var newNode;
4304         if(this.useDom || o.ns){
4305             newNode = createDom(o, null);
4306             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4307         }else{
4308             var html = createHtml(o);
4309             newNode = this.insertHtml(pos, el, html);
4310         }
4311         return returnElement ? Roo.get(newNode, true) : newNode;
4312     },
4313
4314     /**
4315      * Creates new Dom element(s) and appends them to el
4316      * @param {String/HTMLElement/Element} el The context element
4317      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4318      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4319      * @return {HTMLElement/Roo.Element} The new node
4320      */
4321     append : function(el, o, returnElement){
4322         el = Roo.getDom(el);
4323         var newNode;
4324         if(this.useDom || o.ns){
4325             newNode = createDom(o, null);
4326             el.appendChild(newNode);
4327         }else{
4328             var html = createHtml(o);
4329             newNode = this.insertHtml("beforeEnd", el, html);
4330         }
4331         return returnElement ? Roo.get(newNode, true) : newNode;
4332     },
4333
4334     /**
4335      * Creates new Dom element(s) and overwrites the contents of el with them
4336      * @param {String/HTMLElement/Element} el The context element
4337      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4338      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4339      * @return {HTMLElement/Roo.Element} The new node
4340      */
4341     overwrite : function(el, o, returnElement){
4342         el = Roo.getDom(el);
4343         if (o.ns) {
4344           
4345             while (el.childNodes.length) {
4346                 el.removeChild(el.firstChild);
4347             }
4348             createDom(o, el);
4349         } else {
4350             el.innerHTML = createHtml(o);   
4351         }
4352         
4353         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4354     },
4355
4356     /**
4357      * Creates a new Roo.DomHelper.Template from the Dom object spec
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {Roo.DomHelper.Template} The new template
4360      */
4361     createTemplate : function(o){
4362         var html = createHtml(o);
4363         return new Roo.Template(html);
4364     }
4365     };
4366 }();
4367 /*
4368  * Based on:
4369  * Ext JS Library 1.1.1
4370  * Copyright(c) 2006-2007, Ext JS, LLC.
4371  *
4372  * Originally Released Under LGPL - original licence link has changed is not relivant.
4373  *
4374  * Fork - LGPL
4375  * <script type="text/javascript">
4376  */
4377  
4378 /**
4379 * @class Roo.Template
4380 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4381 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4382 * Usage:
4383 <pre><code>
4384 var t = new Roo.Template(
4385     '&lt;div name="{id}"&gt;',
4386         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4387     '&lt;/div&gt;'
4388 );
4389 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4390 </code></pre>
4391 * 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>. 
4392 * @constructor
4393 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4394 */
4395 Roo.Template = function(html){
4396     if(html instanceof Array){
4397         html = html.join("");
4398     }else if(arguments.length > 1){
4399         html = Array.prototype.join.call(arguments, "");
4400     }
4401     /**@private*/
4402     this.html = html;
4403     
4404 };
4405 Roo.Template.prototype = {
4406     /**
4407      * Returns an HTML fragment of this template with the specified values applied.
4408      * @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'})
4409      * @return {String} The HTML fragment
4410      */
4411     applyTemplate : function(values){
4412         if(this.compiled){
4413             return this.compiled(values);
4414         }
4415         var useF = this.disableFormats !== true;
4416         var fm = Roo.util.Format, tpl = this;
4417         var fn = function(m, name, format, args){
4418             if(format && useF){
4419                 if(format.substr(0, 5) == "this."){
4420                     return tpl.call(format.substr(5), values[name], values);
4421                 }else{
4422                     if(args){
4423                         // quoted values are required for strings in compiled templates, 
4424                         // but for non compiled we need to strip them
4425                         // quoted reversed for jsmin
4426                         var re = /^\s*['"](.*)["']\s*$/;
4427                         args = args.split(',');
4428                         for(var i = 0, len = args.length; i < len; i++){
4429                             args[i] = args[i].replace(re, "$1");
4430                         }
4431                         args = [values[name]].concat(args);
4432                     }else{
4433                         args = [values[name]];
4434                     }
4435                     return fm[format].apply(fm, args);
4436                 }
4437             }else{
4438                 return values[name] !== undefined ? values[name] : "";
4439             }
4440         };
4441         return this.html.replace(this.re, fn);
4442     },
4443     
4444     /**
4445      * Sets the HTML used as the template and optionally compiles it.
4446      * @param {String} html
4447      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4448      * @return {Roo.Template} this
4449      */
4450     set : function(html, compile){
4451         this.html = html;
4452         this.compiled = null;
4453         if(compile){
4454             this.compile();
4455         }
4456         return this;
4457     },
4458     
4459     /**
4460      * True to disable format functions (defaults to false)
4461      * @type Boolean
4462      */
4463     disableFormats : false,
4464     
4465     /**
4466     * The regular expression used to match template variables 
4467     * @type RegExp
4468     * @property 
4469     */
4470     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4471     
4472     /**
4473      * Compiles the template into an internal function, eliminating the RegEx overhead.
4474      * @return {Roo.Template} this
4475      */
4476     compile : function(){
4477         var fm = Roo.util.Format;
4478         var useF = this.disableFormats !== true;
4479         var sep = Roo.isGecko ? "+" : ",";
4480         var fn = function(m, name, format, args){
4481             if(format && useF){
4482                 args = args ? ',' + args : "";
4483                 if(format.substr(0, 5) != "this."){
4484                     format = "fm." + format + '(';
4485                 }else{
4486                     format = 'this.call("'+ format.substr(5) + '", ';
4487                     args = ", values";
4488                 }
4489             }else{
4490                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4491             }
4492             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4493         };
4494         var body;
4495         // branched to use + in gecko and [].join() in others
4496         if(Roo.isGecko){
4497             body = "this.compiled = function(values){ return '" +
4498                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4499                     "';};";
4500         }else{
4501             body = ["this.compiled = function(values){ return ['"];
4502             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4503             body.push("'].join('');};");
4504             body = body.join('');
4505         }
4506         /**
4507          * eval:var:values
4508          * eval:var:fm
4509          */
4510         eval(body);
4511         return this;
4512     },
4513     
4514     // private function used to call members
4515     call : function(fnName, value, allValues){
4516         return this[fnName](value, allValues);
4517     },
4518     
4519     /**
4520      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4521      * @param {String/HTMLElement/Roo.Element} el The context element
4522      * @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'})
4523      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4524      * @return {HTMLElement/Roo.Element} The new node or Element
4525      */
4526     insertFirst: function(el, values, returnElement){
4527         return this.doInsert('afterBegin', el, values, returnElement);
4528     },
4529
4530     /**
4531      * Applies the supplied values to the template and inserts the new node(s) before el.
4532      * @param {String/HTMLElement/Roo.Element} el The context element
4533      * @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'})
4534      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4535      * @return {HTMLElement/Roo.Element} The new node or Element
4536      */
4537     insertBefore: function(el, values, returnElement){
4538         return this.doInsert('beforeBegin', el, values, returnElement);
4539     },
4540
4541     /**
4542      * Applies the supplied values to the template and inserts the new node(s) after el.
4543      * @param {String/HTMLElement/Roo.Element} el The context element
4544      * @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'})
4545      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4546      * @return {HTMLElement/Roo.Element} The new node or Element
4547      */
4548     insertAfter : function(el, values, returnElement){
4549         return this.doInsert('afterEnd', el, values, returnElement);
4550     },
4551     
4552     /**
4553      * Applies the supplied values to the template and appends the new node(s) to el.
4554      * @param {String/HTMLElement/Roo.Element} el The context element
4555      * @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'})
4556      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4557      * @return {HTMLElement/Roo.Element} The new node or Element
4558      */
4559     append : function(el, values, returnElement){
4560         return this.doInsert('beforeEnd', el, values, returnElement);
4561     },
4562
4563     doInsert : function(where, el, values, returnEl){
4564         el = Roo.getDom(el);
4565         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4566         return returnEl ? Roo.get(newNode, true) : newNode;
4567     },
4568
4569     /**
4570      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4571      * @param {String/HTMLElement/Roo.Element} el The context element
4572      * @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'})
4573      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4574      * @return {HTMLElement/Roo.Element} The new node or Element
4575      */
4576     overwrite : function(el, values, returnElement){
4577         el = Roo.getDom(el);
4578         el.innerHTML = this.applyTemplate(values);
4579         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4580     }
4581 };
4582 /**
4583  * Alias for {@link #applyTemplate}
4584  * @method
4585  */
4586 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4587
4588 // backwards compat
4589 Roo.DomHelper.Template = Roo.Template;
4590
4591 /**
4592  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4593  * @param {String/HTMLElement} el A DOM element or its id
4594  * @returns {Roo.Template} The created template
4595  * @static
4596  */
4597 Roo.Template.from = function(el){
4598     el = Roo.getDom(el);
4599     return new Roo.Template(el.value || el.innerHTML);
4600 };/*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611
4612 /*
4613  * This is code is also distributed under MIT license for use
4614  * with jQuery and prototype JavaScript libraries.
4615  */
4616 /**
4617  * @class Roo.DomQuery
4618 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).
4619 <p>
4620 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>
4621
4622 <p>
4623 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.
4624 </p>
4625 <h4>Element Selectors:</h4>
4626 <ul class="list">
4627     <li> <b>*</b> any element</li>
4628     <li> <b>E</b> an element with the tag E</li>
4629     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4630     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4631     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4632     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4633 </ul>
4634 <h4>Attribute Selectors:</h4>
4635 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4636 <ul class="list">
4637     <li> <b>E[foo]</b> has an attribute "foo"</li>
4638     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4639     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4640     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4641     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4642     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4643     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4644 </ul>
4645 <h4>Pseudo Classes:</h4>
4646 <ul class="list">
4647     <li> <b>E:first-child</b> E is the first child of its parent</li>
4648     <li> <b>E:last-child</b> E is the last child of its parent</li>
4649     <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>
4650     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4651     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4652     <li> <b>E:only-child</b> E is the only child of its parent</li>
4653     <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>
4654     <li> <b>E:first</b> the first E in the resultset</li>
4655     <li> <b>E:last</b> the last E in the resultset</li>
4656     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4657     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4658     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4659     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4660     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4661     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4662     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4663     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4664     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4665 </ul>
4666 <h4>CSS Value Selectors:</h4>
4667 <ul class="list">
4668     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4669     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4670     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4671     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4672     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4673     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4674 </ul>
4675  * @singleton
4676  */
4677 Roo.DomQuery = function(){
4678     var cache = {}, simpleCache = {}, valueCache = {};
4679     var nonSpace = /\S/;
4680     var trimRe = /^\s+|\s+$/g;
4681     var tplRe = /\{(\d+)\}/g;
4682     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4683     var tagTokenRe = /^(#)?([\w-\*]+)/;
4684     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4685
4686     function child(p, index){
4687         var i = 0;
4688         var n = p.firstChild;
4689         while(n){
4690             if(n.nodeType == 1){
4691                if(++i == index){
4692                    return n;
4693                }
4694             }
4695             n = n.nextSibling;
4696         }
4697         return null;
4698     };
4699
4700     function next(n){
4701         while((n = n.nextSibling) && n.nodeType != 1);
4702         return n;
4703     };
4704
4705     function prev(n){
4706         while((n = n.previousSibling) && n.nodeType != 1);
4707         return n;
4708     };
4709
4710     function children(d){
4711         var n = d.firstChild, ni = -1;
4712             while(n){
4713                 var nx = n.nextSibling;
4714                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4715                     d.removeChild(n);
4716                 }else{
4717                     n.nodeIndex = ++ni;
4718                 }
4719                 n = nx;
4720             }
4721             return this;
4722         };
4723
4724     function byClassName(c, a, v){
4725         if(!v){
4726             return c;
4727         }
4728         var r = [], ri = -1, cn;
4729         for(var i = 0, ci; ci = c[i]; i++){
4730             if((' '+ci.className+' ').indexOf(v) != -1){
4731                 r[++ri] = ci;
4732             }
4733         }
4734         return r;
4735     };
4736
4737     function attrValue(n, attr){
4738         if(!n.tagName && typeof n.length != "undefined"){
4739             n = n[0];
4740         }
4741         if(!n){
4742             return null;
4743         }
4744         if(attr == "for"){
4745             return n.htmlFor;
4746         }
4747         if(attr == "class" || attr == "className"){
4748             return n.className;
4749         }
4750         return n.getAttribute(attr) || n[attr];
4751
4752     };
4753
4754     function getNodes(ns, mode, tagName){
4755         var result = [], ri = -1, cs;
4756         if(!ns){
4757             return result;
4758         }
4759         tagName = tagName || "*";
4760         if(typeof ns.getElementsByTagName != "undefined"){
4761             ns = [ns];
4762         }
4763         if(!mode){
4764             for(var i = 0, ni; ni = ns[i]; i++){
4765                 cs = ni.getElementsByTagName(tagName);
4766                 for(var j = 0, ci; ci = cs[j]; j++){
4767                     result[++ri] = ci;
4768                 }
4769             }
4770         }else if(mode == "/" || mode == ">"){
4771             var utag = tagName.toUpperCase();
4772             for(var i = 0, ni, cn; ni = ns[i]; i++){
4773                 cn = ni.children || ni.childNodes;
4774                 for(var j = 0, cj; cj = cn[j]; j++){
4775                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4776                         result[++ri] = cj;
4777                     }
4778                 }
4779             }
4780         }else if(mode == "+"){
4781             var utag = tagName.toUpperCase();
4782             for(var i = 0, n; n = ns[i]; i++){
4783                 while((n = n.nextSibling) && n.nodeType != 1);
4784                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4785                     result[++ri] = n;
4786                 }
4787             }
4788         }else if(mode == "~"){
4789             for(var i = 0, n; n = ns[i]; i++){
4790                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4791                 if(n){
4792                     result[++ri] = n;
4793                 }
4794             }
4795         }
4796         return result;
4797     };
4798
4799     function concat(a, b){
4800         if(b.slice){
4801             return a.concat(b);
4802         }
4803         for(var i = 0, l = b.length; i < l; i++){
4804             a[a.length] = b[i];
4805         }
4806         return a;
4807     }
4808
4809     function byTag(cs, tagName){
4810         if(cs.tagName || cs == document){
4811             cs = [cs];
4812         }
4813         if(!tagName){
4814             return cs;
4815         }
4816         var r = [], ri = -1;
4817         tagName = tagName.toLowerCase();
4818         for(var i = 0, ci; ci = cs[i]; i++){
4819             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4820                 r[++ri] = ci;
4821             }
4822         }
4823         return r;
4824     };
4825
4826     function byId(cs, attr, id){
4827         if(cs.tagName || cs == document){
4828             cs = [cs];
4829         }
4830         if(!id){
4831             return cs;
4832         }
4833         var r = [], ri = -1;
4834         for(var i = 0,ci; ci = cs[i]; i++){
4835             if(ci && ci.id == id){
4836                 r[++ri] = ci;
4837                 return r;
4838             }
4839         }
4840         return r;
4841     };
4842
4843     function byAttribute(cs, attr, value, op, custom){
4844         var r = [], ri = -1, st = custom=="{";
4845         var f = Roo.DomQuery.operators[op];
4846         for(var i = 0, ci; ci = cs[i]; i++){
4847             var a;
4848             if(st){
4849                 a = Roo.DomQuery.getStyle(ci, attr);
4850             }
4851             else if(attr == "class" || attr == "className"){
4852                 a = ci.className;
4853             }else if(attr == "for"){
4854                 a = ci.htmlFor;
4855             }else if(attr == "href"){
4856                 a = ci.getAttribute("href", 2);
4857             }else{
4858                 a = ci.getAttribute(attr);
4859             }
4860             if((f && f(a, value)) || (!f && a)){
4861                 r[++ri] = ci;
4862             }
4863         }
4864         return r;
4865     };
4866
4867     function byPseudo(cs, name, value){
4868         return Roo.DomQuery.pseudos[name](cs, value);
4869     };
4870
4871     // This is for IE MSXML which does not support expandos.
4872     // IE runs the same speed using setAttribute, however FF slows way down
4873     // and Safari completely fails so they need to continue to use expandos.
4874     var isIE = window.ActiveXObject ? true : false;
4875
4876     // this eval is stop the compressor from
4877     // renaming the variable to something shorter
4878     
4879     /** eval:var:batch */
4880     var batch = 30803; 
4881
4882     var key = 30803;
4883
4884     function nodupIEXml(cs){
4885         var d = ++key;
4886         cs[0].setAttribute("_nodup", d);
4887         var r = [cs[0]];
4888         for(var i = 1, len = cs.length; i < len; i++){
4889             var c = cs[i];
4890             if(!c.getAttribute("_nodup") != d){
4891                 c.setAttribute("_nodup", d);
4892                 r[r.length] = c;
4893             }
4894         }
4895         for(var i = 0, len = cs.length; i < len; i++){
4896             cs[i].removeAttribute("_nodup");
4897         }
4898         return r;
4899     }
4900
4901     function nodup(cs){
4902         if(!cs){
4903             return [];
4904         }
4905         var len = cs.length, c, i, r = cs, cj, ri = -1;
4906         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4907             return cs;
4908         }
4909         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4910             return nodupIEXml(cs);
4911         }
4912         var d = ++key;
4913         cs[0]._nodup = d;
4914         for(i = 1; c = cs[i]; i++){
4915             if(c._nodup != d){
4916                 c._nodup = d;
4917             }else{
4918                 r = [];
4919                 for(var j = 0; j < i; j++){
4920                     r[++ri] = cs[j];
4921                 }
4922                 for(j = i+1; cj = cs[j]; j++){
4923                     if(cj._nodup != d){
4924                         cj._nodup = d;
4925                         r[++ri] = cj;
4926                     }
4927                 }
4928                 return r;
4929             }
4930         }
4931         return r;
4932     }
4933
4934     function quickDiffIEXml(c1, c2){
4935         var d = ++key;
4936         for(var i = 0, len = c1.length; i < len; i++){
4937             c1[i].setAttribute("_qdiff", d);
4938         }
4939         var r = [];
4940         for(var i = 0, len = c2.length; i < len; i++){
4941             if(c2[i].getAttribute("_qdiff") != d){
4942                 r[r.length] = c2[i];
4943             }
4944         }
4945         for(var i = 0, len = c1.length; i < len; i++){
4946            c1[i].removeAttribute("_qdiff");
4947         }
4948         return r;
4949     }
4950
4951     function quickDiff(c1, c2){
4952         var len1 = c1.length;
4953         if(!len1){
4954             return c2;
4955         }
4956         if(isIE && c1[0].selectSingleNode){
4957             return quickDiffIEXml(c1, c2);
4958         }
4959         var d = ++key;
4960         for(var i = 0; i < len1; i++){
4961             c1[i]._qdiff = d;
4962         }
4963         var r = [];
4964         for(var i = 0, len = c2.length; i < len; i++){
4965             if(c2[i]._qdiff != d){
4966                 r[r.length] = c2[i];
4967             }
4968         }
4969         return r;
4970     }
4971
4972     function quickId(ns, mode, root, id){
4973         if(ns == root){
4974            var d = root.ownerDocument || root;
4975            return d.getElementById(id);
4976         }
4977         ns = getNodes(ns, mode, "*");
4978         return byId(ns, null, id);
4979     }
4980
4981     return {
4982         getStyle : function(el, name){
4983             return Roo.fly(el).getStyle(name);
4984         },
4985         /**
4986          * Compiles a selector/xpath query into a reusable function. The returned function
4987          * takes one parameter "root" (optional), which is the context node from where the query should start.
4988          * @param {String} selector The selector/xpath query
4989          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4990          * @return {Function}
4991          */
4992         compile : function(path, type){
4993             type = type || "select";
4994             
4995             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4996             var q = path, mode, lq;
4997             var tk = Roo.DomQuery.matchers;
4998             var tklen = tk.length;
4999             var mm;
5000
5001             // accept leading mode switch
5002             var lmode = q.match(modeRe);
5003             if(lmode && lmode[1]){
5004                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5005                 q = q.replace(lmode[1], "");
5006             }
5007             // strip leading slashes
5008             while(path.substr(0, 1)=="/"){
5009                 path = path.substr(1);
5010             }
5011
5012             while(q && lq != q){
5013                 lq = q;
5014                 var tm = q.match(tagTokenRe);
5015                 if(type == "select"){
5016                     if(tm){
5017                         if(tm[1] == "#"){
5018                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5019                         }else{
5020                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5021                         }
5022                         q = q.replace(tm[0], "");
5023                     }else if(q.substr(0, 1) != '@'){
5024                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5025                     }
5026                 }else{
5027                     if(tm){
5028                         if(tm[1] == "#"){
5029                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5030                         }else{
5031                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5032                         }
5033                         q = q.replace(tm[0], "");
5034                     }
5035                 }
5036                 while(!(mm = q.match(modeRe))){
5037                     var matched = false;
5038                     for(var j = 0; j < tklen; j++){
5039                         var t = tk[j];
5040                         var m = q.match(t.re);
5041                         if(m){
5042                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5043                                                     return m[i];
5044                                                 });
5045                             q = q.replace(m[0], "");
5046                             matched = true;
5047                             break;
5048                         }
5049                     }
5050                     // prevent infinite loop on bad selector
5051                     if(!matched){
5052                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5053                     }
5054                 }
5055                 if(mm[1]){
5056                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5057                     q = q.replace(mm[1], "");
5058                 }
5059             }
5060             fn[fn.length] = "return nodup(n);\n}";
5061             
5062              /** 
5063               * list of variables that need from compression as they are used by eval.
5064              *  eval:var:batch 
5065              *  eval:var:nodup
5066              *  eval:var:byTag
5067              *  eval:var:ById
5068              *  eval:var:getNodes
5069              *  eval:var:quickId
5070              *  eval:var:mode
5071              *  eval:var:root
5072              *  eval:var:n
5073              *  eval:var:byClassName
5074              *  eval:var:byPseudo
5075              *  eval:var:byAttribute
5076              *  eval:var:attrValue
5077              * 
5078              **/ 
5079             eval(fn.join(""));
5080             return f;
5081         },
5082
5083         /**
5084          * Selects a group of elements.
5085          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5086          * @param {Node} root (optional) The start of the query (defaults to document).
5087          * @return {Array}
5088          */
5089         select : function(path, root, type){
5090             if(!root || root == document){
5091                 root = document;
5092             }
5093             if(typeof root == "string"){
5094                 root = document.getElementById(root);
5095             }
5096             var paths = path.split(",");
5097             var results = [];
5098             for(var i = 0, len = paths.length; i < len; i++){
5099                 var p = paths[i].replace(trimRe, "");
5100                 if(!cache[p]){
5101                     cache[p] = Roo.DomQuery.compile(p);
5102                     if(!cache[p]){
5103                         throw p + " is not a valid selector";
5104                     }
5105                 }
5106                 var result = cache[p](root);
5107                 if(result && result != document){
5108                     results = results.concat(result);
5109                 }
5110             }
5111             if(paths.length > 1){
5112                 return nodup(results);
5113             }
5114             return results;
5115         },
5116
5117         /**
5118          * Selects a single element.
5119          * @param {String} selector The selector/xpath query
5120          * @param {Node} root (optional) The start of the query (defaults to document).
5121          * @return {Element}
5122          */
5123         selectNode : function(path, root){
5124             return Roo.DomQuery.select(path, root)[0];
5125         },
5126
5127         /**
5128          * Selects the value of a node, optionally replacing null with the defaultValue.
5129          * @param {String} selector The selector/xpath query
5130          * @param {Node} root (optional) The start of the query (defaults to document).
5131          * @param {String} defaultValue
5132          */
5133         selectValue : function(path, root, defaultValue){
5134             path = path.replace(trimRe, "");
5135             if(!valueCache[path]){
5136                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5137             }
5138             var n = valueCache[path](root);
5139             n = n[0] ? n[0] : n;
5140             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5141             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5142         },
5143
5144         /**
5145          * Selects the value of a node, parsing integers and floats.
5146          * @param {String} selector The selector/xpath query
5147          * @param {Node} root (optional) The start of the query (defaults to document).
5148          * @param {Number} defaultValue
5149          * @return {Number}
5150          */
5151         selectNumber : function(path, root, defaultValue){
5152             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5153             return parseFloat(v);
5154         },
5155
5156         /**
5157          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5158          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5159          * @param {String} selector The simple selector to test
5160          * @return {Boolean}
5161          */
5162         is : function(el, ss){
5163             if(typeof el == "string"){
5164                 el = document.getElementById(el);
5165             }
5166             var isArray = (el instanceof Array);
5167             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5168             return isArray ? (result.length == el.length) : (result.length > 0);
5169         },
5170
5171         /**
5172          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5173          * @param {Array} el An array of elements to filter
5174          * @param {String} selector The simple selector to test
5175          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5176          * the selector instead of the ones that match
5177          * @return {Array}
5178          */
5179         filter : function(els, ss, nonMatches){
5180             ss = ss.replace(trimRe, "");
5181             if(!simpleCache[ss]){
5182                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5183             }
5184             var result = simpleCache[ss](els);
5185             return nonMatches ? quickDiff(result, els) : result;
5186         },
5187
5188         /**
5189          * Collection of matching regular expressions and code snippets.
5190          */
5191         matchers : [{
5192                 re: /^\.([\w-]+)/,
5193                 select: 'n = byClassName(n, null, " {1} ");'
5194             }, {
5195                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5196                 select: 'n = byPseudo(n, "{1}", "{2}");'
5197             },{
5198                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5199                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5200             }, {
5201                 re: /^#([\w-]+)/,
5202                 select: 'n = byId(n, null, "{1}");'
5203             },{
5204                 re: /^@([\w-]+)/,
5205                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5206             }
5207         ],
5208
5209         /**
5210          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5211          * 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;.
5212          */
5213         operators : {
5214             "=" : function(a, v){
5215                 return a == v;
5216             },
5217             "!=" : function(a, v){
5218                 return a != v;
5219             },
5220             "^=" : function(a, v){
5221                 return a && a.substr(0, v.length) == v;
5222             },
5223             "$=" : function(a, v){
5224                 return a && a.substr(a.length-v.length) == v;
5225             },
5226             "*=" : function(a, v){
5227                 return a && a.indexOf(v) !== -1;
5228             },
5229             "%=" : function(a, v){
5230                 return (a % v) == 0;
5231             },
5232             "|=" : function(a, v){
5233                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5234             },
5235             "~=" : function(a, v){
5236                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5237             }
5238         },
5239
5240         /**
5241          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5242          * and the argument (if any) supplied in the selector.
5243          */
5244         pseudos : {
5245             "first-child" : function(c){
5246                 var r = [], ri = -1, n;
5247                 for(var i = 0, ci; ci = n = c[i]; i++){
5248                     while((n = n.previousSibling) && n.nodeType != 1);
5249                     if(!n){
5250                         r[++ri] = ci;
5251                     }
5252                 }
5253                 return r;
5254             },
5255
5256             "last-child" : function(c){
5257                 var r = [], ri = -1, n;
5258                 for(var i = 0, ci; ci = n = c[i]; i++){
5259                     while((n = n.nextSibling) && n.nodeType != 1);
5260                     if(!n){
5261                         r[++ri] = ci;
5262                     }
5263                 }
5264                 return r;
5265             },
5266
5267             "nth-child" : function(c, a) {
5268                 var r = [], ri = -1;
5269                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5270                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5271                 for(var i = 0, n; n = c[i]; i++){
5272                     var pn = n.parentNode;
5273                     if (batch != pn._batch) {
5274                         var j = 0;
5275                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5276                             if(cn.nodeType == 1){
5277                                cn.nodeIndex = ++j;
5278                             }
5279                         }
5280                         pn._batch = batch;
5281                     }
5282                     if (f == 1) {
5283                         if (l == 0 || n.nodeIndex == l){
5284                             r[++ri] = n;
5285                         }
5286                     } else if ((n.nodeIndex + l) % f == 0){
5287                         r[++ri] = n;
5288                     }
5289                 }
5290
5291                 return r;
5292             },
5293
5294             "only-child" : function(c){
5295                 var r = [], ri = -1;;
5296                 for(var i = 0, ci; ci = c[i]; i++){
5297                     if(!prev(ci) && !next(ci)){
5298                         r[++ri] = ci;
5299                     }
5300                 }
5301                 return r;
5302             },
5303
5304             "empty" : function(c){
5305                 var r = [], ri = -1;
5306                 for(var i = 0, ci; ci = c[i]; i++){
5307                     var cns = ci.childNodes, j = 0, cn, empty = true;
5308                     while(cn = cns[j]){
5309                         ++j;
5310                         if(cn.nodeType == 1 || cn.nodeType == 3){
5311                             empty = false;
5312                             break;
5313                         }
5314                     }
5315                     if(empty){
5316                         r[++ri] = ci;
5317                     }
5318                 }
5319                 return r;
5320             },
5321
5322             "contains" : function(c, v){
5323                 var r = [], ri = -1;
5324                 for(var i = 0, ci; ci = c[i]; i++){
5325                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5326                         r[++ri] = ci;
5327                     }
5328                 }
5329                 return r;
5330             },
5331
5332             "nodeValue" : function(c, v){
5333                 var r = [], ri = -1;
5334                 for(var i = 0, ci; ci = c[i]; i++){
5335                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5336                         r[++ri] = ci;
5337                     }
5338                 }
5339                 return r;
5340             },
5341
5342             "checked" : function(c){
5343                 var r = [], ri = -1;
5344                 for(var i = 0, ci; ci = c[i]; i++){
5345                     if(ci.checked == true){
5346                         r[++ri] = ci;
5347                     }
5348                 }
5349                 return r;
5350             },
5351
5352             "not" : function(c, ss){
5353                 return Roo.DomQuery.filter(c, ss, true);
5354             },
5355
5356             "odd" : function(c){
5357                 return this["nth-child"](c, "odd");
5358             },
5359
5360             "even" : function(c){
5361                 return this["nth-child"](c, "even");
5362             },
5363
5364             "nth" : function(c, a){
5365                 return c[a-1] || [];
5366             },
5367
5368             "first" : function(c){
5369                 return c[0] || [];
5370             },
5371
5372             "last" : function(c){
5373                 return c[c.length-1] || [];
5374             },
5375
5376             "has" : function(c, ss){
5377                 var s = Roo.DomQuery.select;
5378                 var r = [], ri = -1;
5379                 for(var i = 0, ci; ci = c[i]; i++){
5380                     if(s(ss, ci).length > 0){
5381                         r[++ri] = ci;
5382                     }
5383                 }
5384                 return r;
5385             },
5386
5387             "next" : function(c, ss){
5388                 var is = Roo.DomQuery.is;
5389                 var r = [], ri = -1;
5390                 for(var i = 0, ci; ci = c[i]; i++){
5391                     var n = next(ci);
5392                     if(n && is(n, ss)){
5393                         r[++ri] = ci;
5394                     }
5395                 }
5396                 return r;
5397             },
5398
5399             "prev" : function(c, ss){
5400                 var is = Roo.DomQuery.is;
5401                 var r = [], ri = -1;
5402                 for(var i = 0, ci; ci = c[i]; i++){
5403                     var n = prev(ci);
5404                     if(n && is(n, ss)){
5405                         r[++ri] = ci;
5406                     }
5407                 }
5408                 return r;
5409             }
5410         }
5411     };
5412 }();
5413
5414 /**
5415  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5416  * @param {String} path The selector/xpath query
5417  * @param {Node} root (optional) The start of the query (defaults to document).
5418  * @return {Array}
5419  * @member Roo
5420  * @method query
5421  */
5422 Roo.query = Roo.DomQuery.select;
5423 /*
5424  * Based on:
5425  * Ext JS Library 1.1.1
5426  * Copyright(c) 2006-2007, Ext JS, LLC.
5427  *
5428  * Originally Released Under LGPL - original licence link has changed is not relivant.
5429  *
5430  * Fork - LGPL
5431  * <script type="text/javascript">
5432  */
5433
5434 /**
5435  * @class Roo.util.Observable
5436  * Base class that provides a common interface for publishing events. Subclasses are expected to
5437  * to have a property "events" with all the events defined.<br>
5438  * For example:
5439  * <pre><code>
5440  Employee = function(name){
5441     this.name = name;
5442     this.addEvents({
5443         "fired" : true,
5444         "quit" : true
5445     });
5446  }
5447  Roo.extend(Employee, Roo.util.Observable);
5448 </code></pre>
5449  * @param {Object} config properties to use (incuding events / listeners)
5450  */
5451
5452 Roo.util.Observable = function(cfg){
5453     
5454     cfg = cfg|| {};
5455     this.addEvents(cfg.events || {});
5456     if (cfg.events) {
5457         delete cfg.events; // make sure
5458     }
5459      
5460     Roo.apply(this, cfg);
5461     
5462     if(this.listeners){
5463         this.on(this.listeners);
5464         delete this.listeners;
5465     }
5466 };
5467 Roo.util.Observable.prototype = {
5468     /** 
5469  * @cfg {Object} listeners  list of events and functions to call for this object, 
5470  * For example :
5471  * <pre><code>
5472     listeners :  { 
5473        'click' : function(e) {
5474            ..... 
5475         } ,
5476         .... 
5477     } 
5478   </code></pre>
5479  */
5480     
5481     
5482     /**
5483      * Fires the specified event with the passed parameters (minus the event name).
5484      * @param {String} eventName
5485      * @param {Object...} args Variable number of parameters are passed to handlers
5486      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5487      */
5488     fireEvent : function(){
5489         var ce = this.events[arguments[0].toLowerCase()];
5490         if(typeof ce == "object"){
5491             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5492         }else{
5493             return true;
5494         }
5495     },
5496
5497     // private
5498     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5499
5500     /**
5501      * Appends an event handler to this component
5502      * @param {String}   eventName The type of event to listen for
5503      * @param {Function} handler The method the event invokes
5504      * @param {Object}   scope (optional) The scope in which to execute the handler
5505      * function. The handler function's "this" context.
5506      * @param {Object}   options (optional) An object containing handler configuration
5507      * properties. This may contain any of the following properties:<ul>
5508      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5509      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5510      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5511      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5512      * by the specified number of milliseconds. If the event fires again within that time, the original
5513      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5514      * </ul><br>
5515      * <p>
5516      * <b>Combining Options</b><br>
5517      * Using the options argument, it is possible to combine different types of listeners:<br>
5518      * <br>
5519      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5520                 <pre><code>
5521                 el.on('click', this.onClick, this, {
5522                         single: true,
5523                 delay: 100,
5524                 forumId: 4
5525                 });
5526                 </code></pre>
5527      * <p>
5528      * <b>Attaching multiple handlers in 1 call</b><br>
5529      * The method also allows for a single argument to be passed which is a config object containing properties
5530      * which specify multiple handlers.
5531      * <pre><code>
5532                 el.on({
5533                         'click': {
5534                         fn: this.onClick,
5535                         scope: this,
5536                         delay: 100
5537                 }, 
5538                 'mouseover': {
5539                         fn: this.onMouseOver,
5540                         scope: this
5541                 },
5542                 'mouseout': {
5543                         fn: this.onMouseOut,
5544                         scope: this
5545                 }
5546                 });
5547                 </code></pre>
5548      * <p>
5549      * Or a shorthand syntax which passes the same scope object to all handlers:
5550         <pre><code>
5551                 el.on({
5552                         'click': this.onClick,
5553                 'mouseover': this.onMouseOver,
5554                 'mouseout': this.onMouseOut,
5555                 scope: this
5556                 });
5557                 </code></pre>
5558      */
5559     addListener : function(eventName, fn, scope, o){
5560         if(typeof eventName == "object"){
5561             o = eventName;
5562             for(var e in o){
5563                 if(this.filterOptRe.test(e)){
5564                     continue;
5565                 }
5566                 if(typeof o[e] == "function"){
5567                     // shared options
5568                     this.addListener(e, o[e], o.scope,  o);
5569                 }else{
5570                     // individual options
5571                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5572                 }
5573             }
5574             return;
5575         }
5576         o = (!o || typeof o == "boolean") ? {} : o;
5577         eventName = eventName.toLowerCase();
5578         var ce = this.events[eventName] || true;
5579         if(typeof ce == "boolean"){
5580             ce = new Roo.util.Event(this, eventName);
5581             this.events[eventName] = ce;
5582         }
5583         ce.addListener(fn, scope, o);
5584     },
5585
5586     /**
5587      * Removes a listener
5588      * @param {String}   eventName     The type of event to listen for
5589      * @param {Function} handler        The handler to remove
5590      * @param {Object}   scope  (optional) The scope (this object) for the handler
5591      */
5592     removeListener : function(eventName, fn, scope){
5593         var ce = this.events[eventName.toLowerCase()];
5594         if(typeof ce == "object"){
5595             ce.removeListener(fn, scope);
5596         }
5597     },
5598
5599     /**
5600      * Removes all listeners for this object
5601      */
5602     purgeListeners : function(){
5603         for(var evt in this.events){
5604             if(typeof this.events[evt] == "object"){
5605                  this.events[evt].clearListeners();
5606             }
5607         }
5608     },
5609
5610     relayEvents : function(o, events){
5611         var createHandler = function(ename){
5612             return function(){
5613                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5614             };
5615         };
5616         for(var i = 0, len = events.length; i < len; i++){
5617             var ename = events[i];
5618             if(!this.events[ename]){ this.events[ename] = true; };
5619             o.on(ename, createHandler(ename), this);
5620         }
5621     },
5622
5623     /**
5624      * Used to define events on this Observable
5625      * @param {Object} object The object with the events defined
5626      */
5627     addEvents : function(o){
5628         if(!this.events){
5629             this.events = {};
5630         }
5631         Roo.applyIf(this.events, o);
5632     },
5633
5634     /**
5635      * Checks to see if this object has any listeners for a specified event
5636      * @param {String} eventName The name of the event to check for
5637      * @return {Boolean} True if the event is being listened for, else false
5638      */
5639     hasListener : function(eventName){
5640         var e = this.events[eventName];
5641         return typeof e == "object" && e.listeners.length > 0;
5642     }
5643 };
5644 /**
5645  * Appends an event handler to this element (shorthand for addListener)
5646  * @param {String}   eventName     The type of event to listen for
5647  * @param {Function} handler        The method the event invokes
5648  * @param {Object}   scope (optional) The scope in which to execute the handler
5649  * function. The handler function's "this" context.
5650  * @param {Object}   options  (optional)
5651  * @method
5652  */
5653 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5654 /**
5655  * Removes a listener (shorthand for removeListener)
5656  * @param {String}   eventName     The type of event to listen for
5657  * @param {Function} handler        The handler to remove
5658  * @param {Object}   scope  (optional) The scope (this object) for the handler
5659  * @method
5660  */
5661 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5662
5663 /**
5664  * Starts capture on the specified Observable. All events will be passed
5665  * to the supplied function with the event name + standard signature of the event
5666  * <b>before</b> the event is fired. If the supplied function returns false,
5667  * the event will not fire.
5668  * @param {Observable} o The Observable to capture
5669  * @param {Function} fn The function to call
5670  * @param {Object} scope (optional) The scope (this object) for the fn
5671  * @static
5672  */
5673 Roo.util.Observable.capture = function(o, fn, scope){
5674     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5675 };
5676
5677 /**
5678  * Removes <b>all</b> added captures from the Observable.
5679  * @param {Observable} o The Observable to release
5680  * @static
5681  */
5682 Roo.util.Observable.releaseCapture = function(o){
5683     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5684 };
5685
5686 (function(){
5687
5688     var createBuffered = function(h, o, scope){
5689         var task = new Roo.util.DelayedTask();
5690         return function(){
5691             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5692         };
5693     };
5694
5695     var createSingle = function(h, e, fn, scope){
5696         return function(){
5697             e.removeListener(fn, scope);
5698             return h.apply(scope, arguments);
5699         };
5700     };
5701
5702     var createDelayed = function(h, o, scope){
5703         return function(){
5704             var args = Array.prototype.slice.call(arguments, 0);
5705             setTimeout(function(){
5706                 h.apply(scope, args);
5707             }, o.delay || 10);
5708         };
5709     };
5710
5711     Roo.util.Event = function(obj, name){
5712         this.name = name;
5713         this.obj = obj;
5714         this.listeners = [];
5715     };
5716
5717     Roo.util.Event.prototype = {
5718         addListener : function(fn, scope, options){
5719             var o = options || {};
5720             scope = scope || this.obj;
5721             if(!this.isListening(fn, scope)){
5722                 var l = {fn: fn, scope: scope, options: o};
5723                 var h = fn;
5724                 if(o.delay){
5725                     h = createDelayed(h, o, scope);
5726                 }
5727                 if(o.single){
5728                     h = createSingle(h, this, fn, scope);
5729                 }
5730                 if(o.buffer){
5731                     h = createBuffered(h, o, scope);
5732                 }
5733                 l.fireFn = h;
5734                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5735                     this.listeners.push(l);
5736                 }else{
5737                     this.listeners = this.listeners.slice(0);
5738                     this.listeners.push(l);
5739                 }
5740             }
5741         },
5742
5743         findListener : function(fn, scope){
5744             scope = scope || this.obj;
5745             var ls = this.listeners;
5746             for(var i = 0, len = ls.length; i < len; i++){
5747                 var l = ls[i];
5748                 if(l.fn == fn && l.scope == scope){
5749                     return i;
5750                 }
5751             }
5752             return -1;
5753         },
5754
5755         isListening : function(fn, scope){
5756             return this.findListener(fn, scope) != -1;
5757         },
5758
5759         removeListener : function(fn, scope){
5760             var index;
5761             if((index = this.findListener(fn, scope)) != -1){
5762                 if(!this.firing){
5763                     this.listeners.splice(index, 1);
5764                 }else{
5765                     this.listeners = this.listeners.slice(0);
5766                     this.listeners.splice(index, 1);
5767                 }
5768                 return true;
5769             }
5770             return false;
5771         },
5772
5773         clearListeners : function(){
5774             this.listeners = [];
5775         },
5776
5777         fire : function(){
5778             var ls = this.listeners, scope, len = ls.length;
5779             if(len > 0){
5780                 this.firing = true;
5781                 var args = Array.prototype.slice.call(arguments, 0);
5782                 for(var i = 0; i < len; i++){
5783                     var l = ls[i];
5784                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5785                         this.firing = false;
5786                         return false;
5787                     }
5788                 }
5789                 this.firing = false;
5790             }
5791             return true;
5792         }
5793     };
5794 })();/*
5795  * Based on:
5796  * Ext JS Library 1.1.1
5797  * Copyright(c) 2006-2007, Ext JS, LLC.
5798  *
5799  * Originally Released Under LGPL - original licence link has changed is not relivant.
5800  *
5801  * Fork - LGPL
5802  * <script type="text/javascript">
5803  */
5804
5805 /**
5806  * @class Roo.EventManager
5807  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5808  * several useful events directly.
5809  * See {@link Roo.EventObject} for more details on normalized event objects.
5810  * @singleton
5811  */
5812 Roo.EventManager = function(){
5813     var docReadyEvent, docReadyProcId, docReadyState = false;
5814     var resizeEvent, resizeTask, textEvent, textSize;
5815     var E = Roo.lib.Event;
5816     var D = Roo.lib.Dom;
5817
5818
5819     var fireDocReady = function(){
5820         if(!docReadyState){
5821             docReadyState = true;
5822             Roo.isReady = true;
5823             if(docReadyProcId){
5824                 clearInterval(docReadyProcId);
5825             }
5826             if(Roo.isGecko || Roo.isOpera) {
5827                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5828             }
5829             if(Roo.isIE){
5830                 var defer = document.getElementById("ie-deferred-loader");
5831                 if(defer){
5832                     defer.onreadystatechange = null;
5833                     defer.parentNode.removeChild(defer);
5834                 }
5835             }
5836             if(docReadyEvent){
5837                 docReadyEvent.fire();
5838                 docReadyEvent.clearListeners();
5839             }
5840         }
5841     };
5842     
5843     var initDocReady = function(){
5844         docReadyEvent = new Roo.util.Event();
5845         if(Roo.isGecko || Roo.isOpera) {
5846             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5847         }else if(Roo.isIE){
5848             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5849             var defer = document.getElementById("ie-deferred-loader");
5850             defer.onreadystatechange = function(){
5851                 if(this.readyState == "complete"){
5852                     fireDocReady();
5853                 }
5854             };
5855         }else if(Roo.isSafari){ 
5856             docReadyProcId = setInterval(function(){
5857                 var rs = document.readyState;
5858                 if(rs == "complete") {
5859                     fireDocReady();     
5860                  }
5861             }, 10);
5862         }
5863         // no matter what, make sure it fires on load
5864         E.on(window, "load", fireDocReady);
5865     };
5866
5867     var createBuffered = function(h, o){
5868         var task = new Roo.util.DelayedTask(h);
5869         return function(e){
5870             // create new event object impl so new events don't wipe out properties
5871             e = new Roo.EventObjectImpl(e);
5872             task.delay(o.buffer, h, null, [e]);
5873         };
5874     };
5875
5876     var createSingle = function(h, el, ename, fn){
5877         return function(e){
5878             Roo.EventManager.removeListener(el, ename, fn);
5879             h(e);
5880         };
5881     };
5882
5883     var createDelayed = function(h, o){
5884         return function(e){
5885             // create new event object impl so new events don't wipe out properties
5886             e = new Roo.EventObjectImpl(e);
5887             setTimeout(function(){
5888                 h(e);
5889             }, o.delay || 10);
5890         };
5891     };
5892
5893     var listen = function(element, ename, opt, fn, scope){
5894         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5895         fn = fn || o.fn; scope = scope || o.scope;
5896         var el = Roo.getDom(element);
5897         if(!el){
5898             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5899         }
5900         var h = function(e){
5901             e = Roo.EventObject.setEvent(e);
5902             var t;
5903             if(o.delegate){
5904                 t = e.getTarget(o.delegate, el);
5905                 if(!t){
5906                     return;
5907                 }
5908             }else{
5909                 t = e.target;
5910             }
5911             if(o.stopEvent === true){
5912                 e.stopEvent();
5913             }
5914             if(o.preventDefault === true){
5915                e.preventDefault();
5916             }
5917             if(o.stopPropagation === true){
5918                 e.stopPropagation();
5919             }
5920
5921             if(o.normalized === false){
5922                 e = e.browserEvent;
5923             }
5924
5925             fn.call(scope || el, e, t, o);
5926         };
5927         if(o.delay){
5928             h = createDelayed(h, o);
5929         }
5930         if(o.single){
5931             h = createSingle(h, el, ename, fn);
5932         }
5933         if(o.buffer){
5934             h = createBuffered(h, o);
5935         }
5936         fn._handlers = fn._handlers || [];
5937         fn._handlers.push([Roo.id(el), ename, h]);
5938
5939         E.on(el, ename, h);
5940         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5941             el.addEventListener("DOMMouseScroll", h, false);
5942             E.on(window, 'unload', function(){
5943                 el.removeEventListener("DOMMouseScroll", h, false);
5944             });
5945         }
5946         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5947             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5948         }
5949         return h;
5950     };
5951
5952     var stopListening = function(el, ename, fn){
5953         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5954         if(hds){
5955             for(var i = 0, len = hds.length; i < len; i++){
5956                 var h = hds[i];
5957                 if(h[0] == id && h[1] == ename){
5958                     hd = h[2];
5959                     hds.splice(i, 1);
5960                     break;
5961                 }
5962             }
5963         }
5964         E.un(el, ename, hd);
5965         el = Roo.getDom(el);
5966         if(ename == "mousewheel" && el.addEventListener){
5967             el.removeEventListener("DOMMouseScroll", hd, false);
5968         }
5969         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5970             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5971         }
5972     };
5973
5974     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5975     
5976     var pub = {
5977         
5978         
5979         /** 
5980          * Fix for doc tools
5981          * @scope Roo.EventManager
5982          */
5983         
5984         
5985         /** 
5986          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5987          * object with a Roo.EventObject
5988          * @param {Function} fn        The method the event invokes
5989          * @param {Object}   scope    An object that becomes the scope of the handler
5990          * @param {boolean}  override If true, the obj passed in becomes
5991          *                             the execution scope of the listener
5992          * @return {Function} The wrapped function
5993          * @deprecated
5994          */
5995         wrap : function(fn, scope, override){
5996             return function(e){
5997                 Roo.EventObject.setEvent(e);
5998                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5999             };
6000         },
6001         
6002         /**
6003      * Appends an event handler to an element (shorthand for addListener)
6004      * @param {String/HTMLElement}   element        The html element or id to assign the
6005      * @param {String}   eventName The type of event to listen for
6006      * @param {Function} handler The method the event invokes
6007      * @param {Object}   scope (optional) The scope in which to execute the handler
6008      * function. The handler function's "this" context.
6009      * @param {Object}   options (optional) An object containing handler configuration
6010      * properties. This may contain any of the following properties:<ul>
6011      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6012      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6013      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6014      * <li>preventDefault {Boolean} True to prevent the default action</li>
6015      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6016      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6017      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6018      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6019      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6020      * by the specified number of milliseconds. If the event fires again within that time, the original
6021      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6022      * </ul><br>
6023      * <p>
6024      * <b>Combining Options</b><br>
6025      * Using the options argument, it is possible to combine different types of listeners:<br>
6026      * <br>
6027      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6028      * Code:<pre><code>
6029 el.on('click', this.onClick, this, {
6030     single: true,
6031     delay: 100,
6032     stopEvent : true,
6033     forumId: 4
6034 });</code></pre>
6035      * <p>
6036      * <b>Attaching multiple handlers in 1 call</b><br>
6037       * The method also allows for a single argument to be passed which is a config object containing properties
6038      * which specify multiple handlers.
6039      * <p>
6040      * Code:<pre><code>
6041 el.on({
6042     'click' : {
6043         fn: this.onClick
6044         scope: this,
6045         delay: 100
6046     },
6047     'mouseover' : {
6048         fn: this.onMouseOver
6049         scope: this
6050     },
6051     'mouseout' : {
6052         fn: this.onMouseOut
6053         scope: this
6054     }
6055 });</code></pre>
6056      * <p>
6057      * Or a shorthand syntax:<br>
6058      * Code:<pre><code>
6059 el.on({
6060     'click' : this.onClick,
6061     'mouseover' : this.onMouseOver,
6062     'mouseout' : this.onMouseOut
6063     scope: this
6064 });</code></pre>
6065      */
6066         addListener : function(element, eventName, fn, scope, options){
6067             if(typeof eventName == "object"){
6068                 var o = eventName;
6069                 for(var e in o){
6070                     if(propRe.test(e)){
6071                         continue;
6072                     }
6073                     if(typeof o[e] == "function"){
6074                         // shared options
6075                         listen(element, e, o, o[e], o.scope);
6076                     }else{
6077                         // individual options
6078                         listen(element, e, o[e]);
6079                     }
6080                 }
6081                 return;
6082             }
6083             return listen(element, eventName, options, fn, scope);
6084         },
6085         
6086         /**
6087          * Removes an event handler
6088          *
6089          * @param {String/HTMLElement}   element        The id or html element to remove the 
6090          *                             event from
6091          * @param {String}   eventName     The type of event
6092          * @param {Function} fn
6093          * @return {Boolean} True if a listener was actually removed
6094          */
6095         removeListener : function(element, eventName, fn){
6096             return stopListening(element, eventName, fn);
6097         },
6098         
6099         /**
6100          * Fires when the document is ready (before onload and before images are loaded). Can be 
6101          * accessed shorthanded Roo.onReady().
6102          * @param {Function} fn        The method the event invokes
6103          * @param {Object}   scope    An  object that becomes the scope of the handler
6104          * @param {boolean}  options
6105          */
6106         onDocumentReady : function(fn, scope, options){
6107             if(docReadyState){ // if it already fired
6108                 docReadyEvent.addListener(fn, scope, options);
6109                 docReadyEvent.fire();
6110                 docReadyEvent.clearListeners();
6111                 return;
6112             }
6113             if(!docReadyEvent){
6114                 initDocReady();
6115             }
6116             docReadyEvent.addListener(fn, scope, options);
6117         },
6118         
6119         /**
6120          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6121          * @param {Function} fn        The method the event invokes
6122          * @param {Object}   scope    An object that becomes the scope of the handler
6123          * @param {boolean}  options
6124          */
6125         onWindowResize : function(fn, scope, options){
6126             if(!resizeEvent){
6127                 resizeEvent = new Roo.util.Event();
6128                 resizeTask = new Roo.util.DelayedTask(function(){
6129                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6130                 });
6131                 E.on(window, "resize", function(){
6132                     if(Roo.isIE){
6133                         resizeTask.delay(50);
6134                     }else{
6135                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6136                     }
6137                 });
6138             }
6139             resizeEvent.addListener(fn, scope, options);
6140         },
6141
6142         /**
6143          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6144          * @param {Function} fn        The method the event invokes
6145          * @param {Object}   scope    An object that becomes the scope of the handler
6146          * @param {boolean}  options
6147          */
6148         onTextResize : function(fn, scope, options){
6149             if(!textEvent){
6150                 textEvent = new Roo.util.Event();
6151                 var textEl = new Roo.Element(document.createElement('div'));
6152                 textEl.dom.className = 'x-text-resize';
6153                 textEl.dom.innerHTML = 'X';
6154                 textEl.appendTo(document.body);
6155                 textSize = textEl.dom.offsetHeight;
6156                 setInterval(function(){
6157                     if(textEl.dom.offsetHeight != textSize){
6158                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6159                     }
6160                 }, this.textResizeInterval);
6161             }
6162             textEvent.addListener(fn, scope, options);
6163         },
6164
6165         /**
6166          * Removes the passed window resize listener.
6167          * @param {Function} fn        The method the event invokes
6168          * @param {Object}   scope    The scope of handler
6169          */
6170         removeResizeListener : function(fn, scope){
6171             if(resizeEvent){
6172                 resizeEvent.removeListener(fn, scope);
6173             }
6174         },
6175
6176         // private
6177         fireResize : function(){
6178             if(resizeEvent){
6179                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6180             }   
6181         },
6182         /**
6183          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6184          */
6185         ieDeferSrc : false,
6186         /**
6187          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6188          */
6189         textResizeInterval : 50
6190     };
6191     
6192     /**
6193      * Fix for doc tools
6194      * @scopeAlias pub=Roo.EventManager
6195      */
6196     
6197      /**
6198      * Appends an event handler to an element (shorthand for addListener)
6199      * @param {String/HTMLElement}   element        The html element or id to assign the
6200      * @param {String}   eventName The type of event to listen for
6201      * @param {Function} handler The method the event invokes
6202      * @param {Object}   scope (optional) The scope in which to execute the handler
6203      * function. The handler function's "this" context.
6204      * @param {Object}   options (optional) An object containing handler configuration
6205      * properties. This may contain any of the following properties:<ul>
6206      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6207      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6208      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6209      * <li>preventDefault {Boolean} True to prevent the default action</li>
6210      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6211      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6212      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6213      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6214      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6215      * by the specified number of milliseconds. If the event fires again within that time, the original
6216      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6217      * </ul><br>
6218      * <p>
6219      * <b>Combining Options</b><br>
6220      * Using the options argument, it is possible to combine different types of listeners:<br>
6221      * <br>
6222      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6223      * Code:<pre><code>
6224 el.on('click', this.onClick, this, {
6225     single: true,
6226     delay: 100,
6227     stopEvent : true,
6228     forumId: 4
6229 });</code></pre>
6230      * <p>
6231      * <b>Attaching multiple handlers in 1 call</b><br>
6232       * The method also allows for a single argument to be passed which is a config object containing properties
6233      * which specify multiple handlers.
6234      * <p>
6235      * Code:<pre><code>
6236 el.on({
6237     'click' : {
6238         fn: this.onClick
6239         scope: this,
6240         delay: 100
6241     },
6242     'mouseover' : {
6243         fn: this.onMouseOver
6244         scope: this
6245     },
6246     'mouseout' : {
6247         fn: this.onMouseOut
6248         scope: this
6249     }
6250 });</code></pre>
6251      * <p>
6252      * Or a shorthand syntax:<br>
6253      * Code:<pre><code>
6254 el.on({
6255     'click' : this.onClick,
6256     'mouseover' : this.onMouseOver,
6257     'mouseout' : this.onMouseOut
6258     scope: this
6259 });</code></pre>
6260      */
6261     pub.on = pub.addListener;
6262     pub.un = pub.removeListener;
6263
6264     pub.stoppedMouseDownEvent = new Roo.util.Event();
6265     return pub;
6266 }();
6267 /**
6268   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6269   * @param {Function} fn        The method the event invokes
6270   * @param {Object}   scope    An  object that becomes the scope of the handler
6271   * @param {boolean}  override If true, the obj passed in becomes
6272   *                             the execution scope of the listener
6273   * @member Roo
6274   * @method onReady
6275  */
6276 Roo.onReady = Roo.EventManager.onDocumentReady;
6277
6278 Roo.onReady(function(){
6279     var bd = Roo.get(document.body);
6280     if(!bd){ return; }
6281
6282     var cls = [
6283             Roo.isIE ? "roo-ie"
6284             : Roo.isGecko ? "roo-gecko"
6285             : Roo.isOpera ? "roo-opera"
6286             : Roo.isSafari ? "roo-safari" : ""];
6287
6288     if(Roo.isMac){
6289         cls.push("roo-mac");
6290     }
6291     if(Roo.isLinux){
6292         cls.push("roo-linux");
6293     }
6294     if(Roo.isBorderBox){
6295         cls.push('roo-border-box');
6296     }
6297     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6298         var p = bd.dom.parentNode;
6299         if(p){
6300             p.className += ' roo-strict';
6301         }
6302     }
6303     bd.addClass(cls.join(' '));
6304 });
6305
6306 /**
6307  * @class Roo.EventObject
6308  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6309  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6310  * Example:
6311  * <pre><code>
6312  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6313     e.preventDefault();
6314     var target = e.getTarget();
6315     ...
6316  }
6317  var myDiv = Roo.get("myDiv");
6318  myDiv.on("click", handleClick);
6319  //or
6320  Roo.EventManager.on("myDiv", 'click', handleClick);
6321  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6322  </code></pre>
6323  * @singleton
6324  */
6325 Roo.EventObject = function(){
6326     
6327     var E = Roo.lib.Event;
6328     
6329     // safari keypress events for special keys return bad keycodes
6330     var safariKeys = {
6331         63234 : 37, // left
6332         63235 : 39, // right
6333         63232 : 38, // up
6334         63233 : 40, // down
6335         63276 : 33, // page up
6336         63277 : 34, // page down
6337         63272 : 46, // delete
6338         63273 : 36, // home
6339         63275 : 35  // end
6340     };
6341
6342     // normalize button clicks
6343     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6344                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6345
6346     Roo.EventObjectImpl = function(e){
6347         if(e){
6348             this.setEvent(e.browserEvent || e);
6349         }
6350     };
6351     Roo.EventObjectImpl.prototype = {
6352         /**
6353          * Used to fix doc tools.
6354          * @scope Roo.EventObject.prototype
6355          */
6356             
6357
6358         
6359         
6360         /** The normal browser event */
6361         browserEvent : null,
6362         /** The button pressed in a mouse event */
6363         button : -1,
6364         /** True if the shift key was down during the event */
6365         shiftKey : false,
6366         /** True if the control key was down during the event */
6367         ctrlKey : false,
6368         /** True if the alt key was down during the event */
6369         altKey : false,
6370
6371         /** Key constant 
6372         * @type Number */
6373         BACKSPACE : 8,
6374         /** Key constant 
6375         * @type Number */
6376         TAB : 9,
6377         /** Key constant 
6378         * @type Number */
6379         RETURN : 13,
6380         /** Key constant 
6381         * @type Number */
6382         ENTER : 13,
6383         /** Key constant 
6384         * @type Number */
6385         SHIFT : 16,
6386         /** Key constant 
6387         * @type Number */
6388         CONTROL : 17,
6389         /** Key constant 
6390         * @type Number */
6391         ESC : 27,
6392         /** Key constant 
6393         * @type Number */
6394         SPACE : 32,
6395         /** Key constant 
6396         * @type Number */
6397         PAGEUP : 33,
6398         /** Key constant 
6399         * @type Number */
6400         PAGEDOWN : 34,
6401         /** Key constant 
6402         * @type Number */
6403         END : 35,
6404         /** Key constant 
6405         * @type Number */
6406         HOME : 36,
6407         /** Key constant 
6408         * @type Number */
6409         LEFT : 37,
6410         /** Key constant 
6411         * @type Number */
6412         UP : 38,
6413         /** Key constant 
6414         * @type Number */
6415         RIGHT : 39,
6416         /** Key constant 
6417         * @type Number */
6418         DOWN : 40,
6419         /** Key constant 
6420         * @type Number */
6421         DELETE : 46,
6422         /** Key constant 
6423         * @type Number */
6424         F5 : 116,
6425
6426            /** @private */
6427         setEvent : function(e){
6428             if(e == this || (e && e.browserEvent)){ // already wrapped
6429                 return e;
6430             }
6431             this.browserEvent = e;
6432             if(e){
6433                 // normalize buttons
6434                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6435                 if(e.type == 'click' && this.button == -1){
6436                     this.button = 0;
6437                 }
6438                 this.type = e.type;
6439                 this.shiftKey = e.shiftKey;
6440                 // mac metaKey behaves like ctrlKey
6441                 this.ctrlKey = e.ctrlKey || e.metaKey;
6442                 this.altKey = e.altKey;
6443                 // in getKey these will be normalized for the mac
6444                 this.keyCode = e.keyCode;
6445                 // keyup warnings on firefox.
6446                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6447                 // cache the target for the delayed and or buffered events
6448                 this.target = E.getTarget(e);
6449                 // same for XY
6450                 this.xy = E.getXY(e);
6451             }else{
6452                 this.button = -1;
6453                 this.shiftKey = false;
6454                 this.ctrlKey = false;
6455                 this.altKey = false;
6456                 this.keyCode = 0;
6457                 this.charCode =0;
6458                 this.target = null;
6459                 this.xy = [0, 0];
6460             }
6461             return this;
6462         },
6463
6464         /**
6465          * Stop the event (preventDefault and stopPropagation)
6466          */
6467         stopEvent : function(){
6468             if(this.browserEvent){
6469                 if(this.browserEvent.type == 'mousedown'){
6470                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6471                 }
6472                 E.stopEvent(this.browserEvent);
6473             }
6474         },
6475
6476         /**
6477          * Prevents the browsers default handling of the event.
6478          */
6479         preventDefault : function(){
6480             if(this.browserEvent){
6481                 E.preventDefault(this.browserEvent);
6482             }
6483         },
6484
6485         /** @private */
6486         isNavKeyPress : function(){
6487             var k = this.keyCode;
6488             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6489             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6490         },
6491
6492         isSpecialKey : function(){
6493             var k = this.keyCode;
6494             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6495             (k == 16) || (k == 17) ||
6496             (k >= 18 && k <= 20) ||
6497             (k >= 33 && k <= 35) ||
6498             (k >= 36 && k <= 39) ||
6499             (k >= 44 && k <= 45);
6500         },
6501         /**
6502          * Cancels bubbling of the event.
6503          */
6504         stopPropagation : function(){
6505             if(this.browserEvent){
6506                 if(this.type == 'mousedown'){
6507                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6508                 }
6509                 E.stopPropagation(this.browserEvent);
6510             }
6511         },
6512
6513         /**
6514          * Gets the key code for the event.
6515          * @return {Number}
6516          */
6517         getCharCode : function(){
6518             return this.charCode || this.keyCode;
6519         },
6520
6521         /**
6522          * Returns a normalized keyCode for the event.
6523          * @return {Number} The key code
6524          */
6525         getKey : function(){
6526             var k = this.keyCode || this.charCode;
6527             return Roo.isSafari ? (safariKeys[k] || k) : k;
6528         },
6529
6530         /**
6531          * Gets the x coordinate of the event.
6532          * @return {Number}
6533          */
6534         getPageX : function(){
6535             return this.xy[0];
6536         },
6537
6538         /**
6539          * Gets the y coordinate of the event.
6540          * @return {Number}
6541          */
6542         getPageY : function(){
6543             return this.xy[1];
6544         },
6545
6546         /**
6547          * Gets the time of the event.
6548          * @return {Number}
6549          */
6550         getTime : function(){
6551             if(this.browserEvent){
6552                 return E.getTime(this.browserEvent);
6553             }
6554             return null;
6555         },
6556
6557         /**
6558          * Gets the page coordinates of the event.
6559          * @return {Array} The xy values like [x, y]
6560          */
6561         getXY : function(){
6562             return this.xy;
6563         },
6564
6565         /**
6566          * Gets the target for the event.
6567          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6568          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6569                 search as a number or element (defaults to 10 || document.body)
6570          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6571          * @return {HTMLelement}
6572          */
6573         getTarget : function(selector, maxDepth, returnEl){
6574             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6575         },
6576         /**
6577          * Gets the related target.
6578          * @return {HTMLElement}
6579          */
6580         getRelatedTarget : function(){
6581             if(this.browserEvent){
6582                 return E.getRelatedTarget(this.browserEvent);
6583             }
6584             return null;
6585         },
6586
6587         /**
6588          * Normalizes mouse wheel delta across browsers
6589          * @return {Number} The delta
6590          */
6591         getWheelDelta : function(){
6592             var e = this.browserEvent;
6593             var delta = 0;
6594             if(e.wheelDelta){ /* IE/Opera. */
6595                 delta = e.wheelDelta/120;
6596             }else if(e.detail){ /* Mozilla case. */
6597                 delta = -e.detail/3;
6598             }
6599             return delta;
6600         },
6601
6602         /**
6603          * Returns true if the control, meta, shift or alt key was pressed during this event.
6604          * @return {Boolean}
6605          */
6606         hasModifier : function(){
6607             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6608         },
6609
6610         /**
6611          * Returns true if the target of this event equals el or is a child of el
6612          * @param {String/HTMLElement/Element} el
6613          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6614          * @return {Boolean}
6615          */
6616         within : function(el, related){
6617             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6618             return t && Roo.fly(el).contains(t);
6619         },
6620
6621         getPoint : function(){
6622             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6623         }
6624     };
6625
6626     return new Roo.EventObjectImpl();
6627 }();
6628             
6629     /*
6630  * Based on:
6631  * Ext JS Library 1.1.1
6632  * Copyright(c) 2006-2007, Ext JS, LLC.
6633  *
6634  * Originally Released Under LGPL - original licence link has changed is not relivant.
6635  *
6636  * Fork - LGPL
6637  * <script type="text/javascript">
6638  */
6639
6640  
6641 // was in Composite Element!??!?!
6642  
6643 (function(){
6644     var D = Roo.lib.Dom;
6645     var E = Roo.lib.Event;
6646     var A = Roo.lib.Anim;
6647
6648     // local style camelizing for speed
6649     var propCache = {};
6650     var camelRe = /(-[a-z])/gi;
6651     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6652     var view = document.defaultView;
6653
6654 /**
6655  * @class Roo.Element
6656  * Represents an Element in the DOM.<br><br>
6657  * Usage:<br>
6658 <pre><code>
6659 var el = Roo.get("my-div");
6660
6661 // or with getEl
6662 var el = getEl("my-div");
6663
6664 // or with a DOM element
6665 var el = Roo.get(myDivElement);
6666 </code></pre>
6667  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6668  * each call instead of constructing a new one.<br><br>
6669  * <b>Animations</b><br />
6670  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6671  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6672 <pre>
6673 Option    Default   Description
6674 --------- --------  ---------------------------------------------
6675 duration  .35       The duration of the animation in seconds
6676 easing    easeOut   The YUI easing method
6677 callback  none      A function to execute when the anim completes
6678 scope     this      The scope (this) of the callback function
6679 </pre>
6680 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6681 * manipulate the animation. Here's an example:
6682 <pre><code>
6683 var el = Roo.get("my-div");
6684
6685 // no animation
6686 el.setWidth(100);
6687
6688 // default animation
6689 el.setWidth(100, true);
6690
6691 // animation with some options set
6692 el.setWidth(100, {
6693     duration: 1,
6694     callback: this.foo,
6695     scope: this
6696 });
6697
6698 // using the "anim" property to get the Anim object
6699 var opt = {
6700     duration: 1,
6701     callback: this.foo,
6702     scope: this
6703 };
6704 el.setWidth(100, opt);
6705 ...
6706 if(opt.anim.isAnimated()){
6707     opt.anim.stop();
6708 }
6709 </code></pre>
6710 * <b> Composite (Collections of) Elements</b><br />
6711  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6712  * @constructor Create a new Element directly.
6713  * @param {String/HTMLElement} element
6714  * @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).
6715  */
6716     Roo.Element = function(element, forceNew){
6717         var dom = typeof element == "string" ?
6718                 document.getElementById(element) : element;
6719         if(!dom){ // invalid id/element
6720             return null;
6721         }
6722         var id = dom.id;
6723         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6724             return Roo.Element.cache[id];
6725         }
6726
6727         /**
6728          * The DOM element
6729          * @type HTMLElement
6730          */
6731         this.dom = dom;
6732
6733         /**
6734          * The DOM element ID
6735          * @type String
6736          */
6737         this.id = id || Roo.id(dom);
6738     };
6739
6740     var El = Roo.Element;
6741
6742     El.prototype = {
6743         /**
6744          * The element's default display mode  (defaults to "")
6745          * @type String
6746          */
6747         originalDisplay : "",
6748
6749         visibilityMode : 1,
6750         /**
6751          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6752          * @type String
6753          */
6754         defaultUnit : "px",
6755         /**
6756          * Sets the element's visibility mode. When setVisible() is called it
6757          * will use this to determine whether to set the visibility or the display property.
6758          * @param visMode Element.VISIBILITY or Element.DISPLAY
6759          * @return {Roo.Element} this
6760          */
6761         setVisibilityMode : function(visMode){
6762             this.visibilityMode = visMode;
6763             return this;
6764         },
6765         /**
6766          * Convenience method for setVisibilityMode(Element.DISPLAY)
6767          * @param {String} display (optional) What to set display to when visible
6768          * @return {Roo.Element} this
6769          */
6770         enableDisplayMode : function(display){
6771             this.setVisibilityMode(El.DISPLAY);
6772             if(typeof display != "undefined") this.originalDisplay = display;
6773             return this;
6774         },
6775
6776         /**
6777          * 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)
6778          * @param {String} selector The simple selector to test
6779          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6780                 search as a number or element (defaults to 10 || document.body)
6781          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6782          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6783          */
6784         findParent : function(simpleSelector, maxDepth, returnEl){
6785             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6786             maxDepth = maxDepth || 50;
6787             if(typeof maxDepth != "number"){
6788                 stopEl = Roo.getDom(maxDepth);
6789                 maxDepth = 10;
6790             }
6791             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6792                 if(dq.is(p, simpleSelector)){
6793                     return returnEl ? Roo.get(p) : p;
6794                 }
6795                 depth++;
6796                 p = p.parentNode;
6797             }
6798             return null;
6799         },
6800
6801
6802         /**
6803          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6804          * @param {String} selector The simple selector to test
6805          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6806                 search as a number or element (defaults to 10 || document.body)
6807          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6808          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6809          */
6810         findParentNode : function(simpleSelector, maxDepth, returnEl){
6811             var p = Roo.fly(this.dom.parentNode, '_internal');
6812             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6813         },
6814
6815         /**
6816          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6817          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6818          * @param {String} selector The simple selector to test
6819          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6820                 search as a number or element (defaults to 10 || document.body)
6821          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6822          */
6823         up : function(simpleSelector, maxDepth){
6824             return this.findParentNode(simpleSelector, maxDepth, true);
6825         },
6826
6827
6828
6829         /**
6830          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6831          * @param {String} selector The simple selector to test
6832          * @return {Boolean} True if this element matches the selector, else false
6833          */
6834         is : function(simpleSelector){
6835             return Roo.DomQuery.is(this.dom, simpleSelector);
6836         },
6837
6838         /**
6839          * Perform animation on this element.
6840          * @param {Object} args The YUI animation control args
6841          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6842          * @param {Function} onComplete (optional) Function to call when animation completes
6843          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6844          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6845          * @return {Roo.Element} this
6846          */
6847         animate : function(args, duration, onComplete, easing, animType){
6848             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6849             return this;
6850         },
6851
6852         /*
6853          * @private Internal animation call
6854          */
6855         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6856             animType = animType || 'run';
6857             opt = opt || {};
6858             var anim = Roo.lib.Anim[animType](
6859                 this.dom, args,
6860                 (opt.duration || defaultDur) || .35,
6861                 (opt.easing || defaultEase) || 'easeOut',
6862                 function(){
6863                     Roo.callback(cb, this);
6864                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6865                 },
6866                 this
6867             );
6868             opt.anim = anim;
6869             return anim;
6870         },
6871
6872         // private legacy anim prep
6873         preanim : function(a, i){
6874             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6875         },
6876
6877         /**
6878          * Removes worthless text nodes
6879          * @param {Boolean} forceReclean (optional) By default the element
6880          * keeps track if it has been cleaned already so
6881          * you can call this over and over. However, if you update the element and
6882          * need to force a reclean, you can pass true.
6883          */
6884         clean : function(forceReclean){
6885             if(this.isCleaned && forceReclean !== true){
6886                 return this;
6887             }
6888             var ns = /\S/;
6889             var d = this.dom, n = d.firstChild, ni = -1;
6890             while(n){
6891                 var nx = n.nextSibling;
6892                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6893                     d.removeChild(n);
6894                 }else{
6895                     n.nodeIndex = ++ni;
6896                 }
6897                 n = nx;
6898             }
6899             this.isCleaned = true;
6900             return this;
6901         },
6902
6903         // private
6904         calcOffsetsTo : function(el){
6905             el = Roo.get(el);
6906             var d = el.dom;
6907             var restorePos = false;
6908             if(el.getStyle('position') == 'static'){
6909                 el.position('relative');
6910                 restorePos = true;
6911             }
6912             var x = 0, y =0;
6913             var op = this.dom;
6914             while(op && op != d && op.tagName != 'HTML'){
6915                 x+= op.offsetLeft;
6916                 y+= op.offsetTop;
6917                 op = op.offsetParent;
6918             }
6919             if(restorePos){
6920                 el.position('static');
6921             }
6922             return [x, y];
6923         },
6924
6925         /**
6926          * Scrolls this element into view within the passed container.
6927          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6928          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6929          * @return {Roo.Element} this
6930          */
6931         scrollIntoView : function(container, hscroll){
6932             var c = Roo.getDom(container) || document.body;
6933             var el = this.dom;
6934
6935             var o = this.calcOffsetsTo(c),
6936                 l = o[0],
6937                 t = o[1],
6938                 b = t+el.offsetHeight,
6939                 r = l+el.offsetWidth;
6940
6941             var ch = c.clientHeight;
6942             var ct = parseInt(c.scrollTop, 10);
6943             var cl = parseInt(c.scrollLeft, 10);
6944             var cb = ct + ch;
6945             var cr = cl + c.clientWidth;
6946
6947             if(t < ct){
6948                 c.scrollTop = t;
6949             }else if(b > cb){
6950                 c.scrollTop = b-ch;
6951             }
6952
6953             if(hscroll !== false){
6954                 if(l < cl){
6955                     c.scrollLeft = l;
6956                 }else if(r > cr){
6957                     c.scrollLeft = r-c.clientWidth;
6958                 }
6959             }
6960             return this;
6961         },
6962
6963         // private
6964         scrollChildIntoView : function(child, hscroll){
6965             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6966         },
6967
6968         /**
6969          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6970          * the new height may not be available immediately.
6971          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6972          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6973          * @param {Function} onComplete (optional) Function to call when animation completes
6974          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6975          * @return {Roo.Element} this
6976          */
6977         autoHeight : function(animate, duration, onComplete, easing){
6978             var oldHeight = this.getHeight();
6979             this.clip();
6980             this.setHeight(1); // force clipping
6981             setTimeout(function(){
6982                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6983                 if(!animate){
6984                     this.setHeight(height);
6985                     this.unclip();
6986                     if(typeof onComplete == "function"){
6987                         onComplete();
6988                     }
6989                 }else{
6990                     this.setHeight(oldHeight); // restore original height
6991                     this.setHeight(height, animate, duration, function(){
6992                         this.unclip();
6993                         if(typeof onComplete == "function") onComplete();
6994                     }.createDelegate(this), easing);
6995                 }
6996             }.createDelegate(this), 0);
6997             return this;
6998         },
6999
7000         /**
7001          * Returns true if this element is an ancestor of the passed element
7002          * @param {HTMLElement/String} el The element to check
7003          * @return {Boolean} True if this element is an ancestor of el, else false
7004          */
7005         contains : function(el){
7006             if(!el){return false;}
7007             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7008         },
7009
7010         /**
7011          * Checks whether the element is currently visible using both visibility and display properties.
7012          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7013          * @return {Boolean} True if the element is currently visible, else false
7014          */
7015         isVisible : function(deep) {
7016             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7017             if(deep !== true || !vis){
7018                 return vis;
7019             }
7020             var p = this.dom.parentNode;
7021             while(p && p.tagName.toLowerCase() != "body"){
7022                 if(!Roo.fly(p, '_isVisible').isVisible()){
7023                     return false;
7024                 }
7025                 p = p.parentNode;
7026             }
7027             return true;
7028         },
7029
7030         /**
7031          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7032          * @param {String} selector The CSS selector
7033          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7034          * @return {CompositeElement/CompositeElementLite} The composite element
7035          */
7036         select : function(selector, unique){
7037             return El.select(selector, unique, this.dom);
7038         },
7039
7040         /**
7041          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7042          * @param {String} selector The CSS selector
7043          * @return {Array} An array of the matched nodes
7044          */
7045         query : function(selector, unique){
7046             return Roo.DomQuery.select(selector, this.dom);
7047         },
7048
7049         /**
7050          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7051          * @param {String} selector The CSS selector
7052          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7053          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7054          */
7055         child : function(selector, returnDom){
7056             var n = Roo.DomQuery.selectNode(selector, this.dom);
7057             return returnDom ? n : Roo.get(n);
7058         },
7059
7060         /**
7061          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7062          * @param {String} selector The CSS selector
7063          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7064          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7065          */
7066         down : function(selector, returnDom){
7067             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7068             return returnDom ? n : Roo.get(n);
7069         },
7070
7071         /**
7072          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7073          * @param {String} group The group the DD object is member of
7074          * @param {Object} config The DD config object
7075          * @param {Object} overrides An object containing methods to override/implement on the DD object
7076          * @return {Roo.dd.DD} The DD object
7077          */
7078         initDD : function(group, config, overrides){
7079             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7080             return Roo.apply(dd, overrides);
7081         },
7082
7083         /**
7084          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7085          * @param {String} group The group the DDProxy object is member of
7086          * @param {Object} config The DDProxy config object
7087          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7088          * @return {Roo.dd.DDProxy} The DDProxy object
7089          */
7090         initDDProxy : function(group, config, overrides){
7091             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7092             return Roo.apply(dd, overrides);
7093         },
7094
7095         /**
7096          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7097          * @param {String} group The group the DDTarget object is member of
7098          * @param {Object} config The DDTarget config object
7099          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7100          * @return {Roo.dd.DDTarget} The DDTarget object
7101          */
7102         initDDTarget : function(group, config, overrides){
7103             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7104             return Roo.apply(dd, overrides);
7105         },
7106
7107         /**
7108          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7109          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7110          * @param {Boolean} visible Whether the element is visible
7111          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7112          * @return {Roo.Element} this
7113          */
7114          setVisible : function(visible, animate){
7115             if(!animate || !A){
7116                 if(this.visibilityMode == El.DISPLAY){
7117                     this.setDisplayed(visible);
7118                 }else{
7119                     this.fixDisplay();
7120                     this.dom.style.visibility = visible ? "visible" : "hidden";
7121                 }
7122             }else{
7123                 // closure for composites
7124                 var dom = this.dom;
7125                 var visMode = this.visibilityMode;
7126                 if(visible){
7127                     this.setOpacity(.01);
7128                     this.setVisible(true);
7129                 }
7130                 this.anim({opacity: { to: (visible?1:0) }},
7131                       this.preanim(arguments, 1),
7132                       null, .35, 'easeIn', function(){
7133                          if(!visible){
7134                              if(visMode == El.DISPLAY){
7135                                  dom.style.display = "none";
7136                              }else{
7137                                  dom.style.visibility = "hidden";
7138                              }
7139                              Roo.get(dom).setOpacity(1);
7140                          }
7141                      });
7142             }
7143             return this;
7144         },
7145
7146         /**
7147          * Returns true if display is not "none"
7148          * @return {Boolean}
7149          */
7150         isDisplayed : function() {
7151             return this.getStyle("display") != "none";
7152         },
7153
7154         /**
7155          * Toggles the element's visibility or display, depending on visibility mode.
7156          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7157          * @return {Roo.Element} this
7158          */
7159         toggle : function(animate){
7160             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7161             return this;
7162         },
7163
7164         /**
7165          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7166          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7167          * @return {Roo.Element} this
7168          */
7169         setDisplayed : function(value) {
7170             if(typeof value == "boolean"){
7171                value = value ? this.originalDisplay : "none";
7172             }
7173             this.setStyle("display", value);
7174             return this;
7175         },
7176
7177         /**
7178          * Tries to focus the element. Any exceptions are caught and ignored.
7179          * @return {Roo.Element} this
7180          */
7181         focus : function() {
7182             try{
7183                 this.dom.focus();
7184             }catch(e){}
7185             return this;
7186         },
7187
7188         /**
7189          * Tries to blur the element. Any exceptions are caught and ignored.
7190          * @return {Roo.Element} this
7191          */
7192         blur : function() {
7193             try{
7194                 this.dom.blur();
7195             }catch(e){}
7196             return this;
7197         },
7198
7199         /**
7200          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7201          * @param {String/Array} className The CSS class to add, or an array of classes
7202          * @return {Roo.Element} this
7203          */
7204         addClass : function(className){
7205             if(className instanceof Array){
7206                 for(var i = 0, len = className.length; i < len; i++) {
7207                     this.addClass(className[i]);
7208                 }
7209             }else{
7210                 if(className && !this.hasClass(className)){
7211                     this.dom.className = this.dom.className + " " + className;
7212                 }
7213             }
7214             return this;
7215         },
7216
7217         /**
7218          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7219          * @param {String/Array} className The CSS class to add, or an array of classes
7220          * @return {Roo.Element} this
7221          */
7222         radioClass : function(className){
7223             var siblings = this.dom.parentNode.childNodes;
7224             for(var i = 0; i < siblings.length; i++) {
7225                 var s = siblings[i];
7226                 if(s.nodeType == 1){
7227                     Roo.get(s).removeClass(className);
7228                 }
7229             }
7230             this.addClass(className);
7231             return this;
7232         },
7233
7234         /**
7235          * Removes one or more CSS classes from the element.
7236          * @param {String/Array} className The CSS class to remove, or an array of classes
7237          * @return {Roo.Element} this
7238          */
7239         removeClass : function(className){
7240             if(!className || !this.dom.className){
7241                 return this;
7242             }
7243             if(className instanceof Array){
7244                 for(var i = 0, len = className.length; i < len; i++) {
7245                     this.removeClass(className[i]);
7246                 }
7247             }else{
7248                 if(this.hasClass(className)){
7249                     var re = this.classReCache[className];
7250                     if (!re) {
7251                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7252                        this.classReCache[className] = re;
7253                     }
7254                     this.dom.className =
7255                         this.dom.className.replace(re, " ");
7256                 }
7257             }
7258             return this;
7259         },
7260
7261         // private
7262         classReCache: {},
7263
7264         /**
7265          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7266          * @param {String} className The CSS class to toggle
7267          * @return {Roo.Element} this
7268          */
7269         toggleClass : function(className){
7270             if(this.hasClass(className)){
7271                 this.removeClass(className);
7272             }else{
7273                 this.addClass(className);
7274             }
7275             return this;
7276         },
7277
7278         /**
7279          * Checks if the specified CSS class exists on this element's DOM node.
7280          * @param {String} className The CSS class to check for
7281          * @return {Boolean} True if the class exists, else false
7282          */
7283         hasClass : function(className){
7284             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7285         },
7286
7287         /**
7288          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7289          * @param {String} oldClassName The CSS class to replace
7290          * @param {String} newClassName The replacement CSS class
7291          * @return {Roo.Element} this
7292          */
7293         replaceClass : function(oldClassName, newClassName){
7294             this.removeClass(oldClassName);
7295             this.addClass(newClassName);
7296             return this;
7297         },
7298
7299         /**
7300          * Returns an object with properties matching the styles requested.
7301          * For example, el.getStyles('color', 'font-size', 'width') might return
7302          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7303          * @param {String} style1 A style name
7304          * @param {String} style2 A style name
7305          * @param {String} etc.
7306          * @return {Object} The style object
7307          */
7308         getStyles : function(){
7309             var a = arguments, len = a.length, r = {};
7310             for(var i = 0; i < len; i++){
7311                 r[a[i]] = this.getStyle(a[i]);
7312             }
7313             return r;
7314         },
7315
7316         /**
7317          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7318          * @param {String} property The style property whose value is returned.
7319          * @return {String} The current value of the style property for this element.
7320          */
7321         getStyle : function(){
7322             return view && view.getComputedStyle ?
7323                 function(prop){
7324                     var el = this.dom, v, cs, camel;
7325                     if(prop == 'float'){
7326                         prop = "cssFloat";
7327                     }
7328                     if(el.style && (v = el.style[prop])){
7329                         return v;
7330                     }
7331                     if(cs = view.getComputedStyle(el, "")){
7332                         if(!(camel = propCache[prop])){
7333                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7334                         }
7335                         return cs[camel];
7336                     }
7337                     return null;
7338                 } :
7339                 function(prop){
7340                     var el = this.dom, v, cs, camel;
7341                     if(prop == 'opacity'){
7342                         if(typeof el.style.filter == 'string'){
7343                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7344                             if(m){
7345                                 var fv = parseFloat(m[1]);
7346                                 if(!isNaN(fv)){
7347                                     return fv ? fv / 100 : 0;
7348                                 }
7349                             }
7350                         }
7351                         return 1;
7352                     }else if(prop == 'float'){
7353                         prop = "styleFloat";
7354                     }
7355                     if(!(camel = propCache[prop])){
7356                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7357                     }
7358                     if(v = el.style[camel]){
7359                         return v;
7360                     }
7361                     if(cs = el.currentStyle){
7362                         return cs[camel];
7363                     }
7364                     return null;
7365                 };
7366         }(),
7367
7368         /**
7369          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7370          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7371          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7372          * @return {Roo.Element} this
7373          */
7374         setStyle : function(prop, value){
7375             if(typeof prop == "string"){
7376                 var camel;
7377                 if(!(camel = propCache[prop])){
7378                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7379                 }
7380                 if(camel == 'opacity') {
7381                     this.setOpacity(value);
7382                 }else{
7383                     this.dom.style[camel] = value;
7384                 }
7385             }else{
7386                 for(var style in prop){
7387                     if(typeof prop[style] != "function"){
7388                        this.setStyle(style, prop[style]);
7389                     }
7390                 }
7391             }
7392             return this;
7393         },
7394
7395         /**
7396          * More flexible version of {@link #setStyle} for setting style properties.
7397          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7398          * a function which returns such a specification.
7399          * @return {Roo.Element} this
7400          */
7401         applyStyles : function(style){
7402             Roo.DomHelper.applyStyles(this.dom, style);
7403             return this;
7404         },
7405
7406         /**
7407           * 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).
7408           * @return {Number} The X position of the element
7409           */
7410         getX : function(){
7411             return D.getX(this.dom);
7412         },
7413
7414         /**
7415           * 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).
7416           * @return {Number} The Y position of the element
7417           */
7418         getY : function(){
7419             return D.getY(this.dom);
7420         },
7421
7422         /**
7423           * 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).
7424           * @return {Array} The XY position of the element
7425           */
7426         getXY : function(){
7427             return D.getXY(this.dom);
7428         },
7429
7430         /**
7431          * 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).
7432          * @param {Number} The X position of the element
7433          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7434          * @return {Roo.Element} this
7435          */
7436         setX : function(x, animate){
7437             if(!animate || !A){
7438                 D.setX(this.dom, x);
7439             }else{
7440                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7441             }
7442             return this;
7443         },
7444
7445         /**
7446          * 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).
7447          * @param {Number} The Y position of the element
7448          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7449          * @return {Roo.Element} this
7450          */
7451         setY : function(y, animate){
7452             if(!animate || !A){
7453                 D.setY(this.dom, y);
7454             }else{
7455                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7456             }
7457             return this;
7458         },
7459
7460         /**
7461          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7462          * @param {String} left The left CSS property value
7463          * @return {Roo.Element} this
7464          */
7465         setLeft : function(left){
7466             this.setStyle("left", this.addUnits(left));
7467             return this;
7468         },
7469
7470         /**
7471          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7472          * @param {String} top The top CSS property value
7473          * @return {Roo.Element} this
7474          */
7475         setTop : function(top){
7476             this.setStyle("top", this.addUnits(top));
7477             return this;
7478         },
7479
7480         /**
7481          * Sets the element's CSS right style.
7482          * @param {String} right The right CSS property value
7483          * @return {Roo.Element} this
7484          */
7485         setRight : function(right){
7486             this.setStyle("right", this.addUnits(right));
7487             return this;
7488         },
7489
7490         /**
7491          * Sets the element's CSS bottom style.
7492          * @param {String} bottom The bottom CSS property value
7493          * @return {Roo.Element} this
7494          */
7495         setBottom : function(bottom){
7496             this.setStyle("bottom", this.addUnits(bottom));
7497             return this;
7498         },
7499
7500         /**
7501          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7502          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7503          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7504          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7505          * @return {Roo.Element} this
7506          */
7507         setXY : function(pos, animate){
7508             if(!animate || !A){
7509                 D.setXY(this.dom, pos);
7510             }else{
7511                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7512             }
7513             return this;
7514         },
7515
7516         /**
7517          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7518          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7519          * @param {Number} x X value for new position (coordinates are page-based)
7520          * @param {Number} y Y value for new position (coordinates are page-based)
7521          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7522          * @return {Roo.Element} this
7523          */
7524         setLocation : function(x, y, animate){
7525             this.setXY([x, y], this.preanim(arguments, 2));
7526             return this;
7527         },
7528
7529         /**
7530          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7531          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7532          * @param {Number} x X value for new position (coordinates are page-based)
7533          * @param {Number} y Y value for new position (coordinates are page-based)
7534          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7535          * @return {Roo.Element} this
7536          */
7537         moveTo : function(x, y, animate){
7538             this.setXY([x, y], this.preanim(arguments, 2));
7539             return this;
7540         },
7541
7542         /**
7543          * Returns the region of the given element.
7544          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7545          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7546          */
7547         getRegion : function(){
7548             return D.getRegion(this.dom);
7549         },
7550
7551         /**
7552          * Returns the offset height of the element
7553          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7554          * @return {Number} The element's height
7555          */
7556         getHeight : function(contentHeight){
7557             var h = this.dom.offsetHeight || 0;
7558             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7559         },
7560
7561         /**
7562          * Returns the offset width of the element
7563          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7564          * @return {Number} The element's width
7565          */
7566         getWidth : function(contentWidth){
7567             var w = this.dom.offsetWidth || 0;
7568             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7569         },
7570
7571         /**
7572          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7573          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7574          * if a height has not been set using CSS.
7575          * @return {Number}
7576          */
7577         getComputedHeight : function(){
7578             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7579             if(!h){
7580                 h = parseInt(this.getStyle('height'), 10) || 0;
7581                 if(!this.isBorderBox()){
7582                     h += this.getFrameWidth('tb');
7583                 }
7584             }
7585             return h;
7586         },
7587
7588         /**
7589          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7590          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7591          * if a width has not been set using CSS.
7592          * @return {Number}
7593          */
7594         getComputedWidth : function(){
7595             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7596             if(!w){
7597                 w = parseInt(this.getStyle('width'), 10) || 0;
7598                 if(!this.isBorderBox()){
7599                     w += this.getFrameWidth('lr');
7600                 }
7601             }
7602             return w;
7603         },
7604
7605         /**
7606          * Returns the size of the element.
7607          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7608          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7609          */
7610         getSize : function(contentSize){
7611             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7612         },
7613
7614         /**
7615          * Returns the width and height of the viewport.
7616          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7617          */
7618         getViewSize : function(){
7619             var d = this.dom, doc = document, aw = 0, ah = 0;
7620             if(d == doc || d == doc.body){
7621                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7622             }else{
7623                 return {
7624                     width : d.clientWidth,
7625                     height: d.clientHeight
7626                 };
7627             }
7628         },
7629
7630         /**
7631          * Returns the value of the "value" attribute
7632          * @param {Boolean} asNumber true to parse the value as a number
7633          * @return {String/Number}
7634          */
7635         getValue : function(asNumber){
7636             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7637         },
7638
7639         // private
7640         adjustWidth : function(width){
7641             if(typeof width == "number"){
7642                 if(this.autoBoxAdjust && !this.isBorderBox()){
7643                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7644                 }
7645                 if(width < 0){
7646                     width = 0;
7647                 }
7648             }
7649             return width;
7650         },
7651
7652         // private
7653         adjustHeight : function(height){
7654             if(typeof height == "number"){
7655                if(this.autoBoxAdjust && !this.isBorderBox()){
7656                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7657                }
7658                if(height < 0){
7659                    height = 0;
7660                }
7661             }
7662             return height;
7663         },
7664
7665         /**
7666          * Set the width of the element
7667          * @param {Number} width The new width
7668          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7669          * @return {Roo.Element} this
7670          */
7671         setWidth : function(width, animate){
7672             width = this.adjustWidth(width);
7673             if(!animate || !A){
7674                 this.dom.style.width = this.addUnits(width);
7675             }else{
7676                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Set the height of the element
7683          * @param {Number} height The new height
7684          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687          setHeight : function(height, animate){
7688             height = this.adjustHeight(height);
7689             if(!animate || !A){
7690                 this.dom.style.height = this.addUnits(height);
7691             }else{
7692                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7693             }
7694             return this;
7695         },
7696
7697         /**
7698          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7699          * @param {Number} width The new width
7700          * @param {Number} height The new height
7701          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704          setSize : function(width, height, animate){
7705             if(typeof width == "object"){ // in case of object from getSize()
7706                 height = width.height; width = width.width;
7707             }
7708             width = this.adjustWidth(width); height = this.adjustHeight(height);
7709             if(!animate || !A){
7710                 this.dom.style.width = this.addUnits(width);
7711                 this.dom.style.height = this.addUnits(height);
7712             }else{
7713                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7714             }
7715             return this;
7716         },
7717
7718         /**
7719          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7720          * @param {Number} x X value for new position (coordinates are page-based)
7721          * @param {Number} y Y value for new position (coordinates are page-based)
7722          * @param {Number} width The new width
7723          * @param {Number} height The new height
7724          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7725          * @return {Roo.Element} this
7726          */
7727         setBounds : function(x, y, width, height, animate){
7728             if(!animate || !A){
7729                 this.setSize(width, height);
7730                 this.setLocation(x, y);
7731             }else{
7732                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7733                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7734                               this.preanim(arguments, 4), 'motion');
7735             }
7736             return this;
7737         },
7738
7739         /**
7740          * 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.
7741          * @param {Roo.lib.Region} region The region to fill
7742          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7743          * @return {Roo.Element} this
7744          */
7745         setRegion : function(region, animate){
7746             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7747             return this;
7748         },
7749
7750         /**
7751          * Appends an event handler
7752          *
7753          * @param {String}   eventName     The type of event to append
7754          * @param {Function} fn        The method the event invokes
7755          * @param {Object} scope       (optional) The scope (this object) of the fn
7756          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7757          */
7758         addListener : function(eventName, fn, scope, options){
7759             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7760         },
7761
7762         /**
7763          * Removes an event handler from this element
7764          * @param {String} eventName the type of event to remove
7765          * @param {Function} fn the method the event invokes
7766          * @return {Roo.Element} this
7767          */
7768         removeListener : function(eventName, fn){
7769             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7770             return this;
7771         },
7772
7773         /**
7774          * Removes all previous added listeners from this element
7775          * @return {Roo.Element} this
7776          */
7777         removeAllListeners : function(){
7778             E.purgeElement(this.dom);
7779             return this;
7780         },
7781
7782         relayEvent : function(eventName, observable){
7783             this.on(eventName, function(e){
7784                 observable.fireEvent(eventName, e);
7785             });
7786         },
7787
7788         /**
7789          * Set the opacity of the element
7790          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7791          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794          setOpacity : function(opacity, animate){
7795             if(!animate || !A){
7796                 var s = this.dom.style;
7797                 if(Roo.isIE){
7798                     s.zoom = 1;
7799                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7800                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7801                 }else{
7802                     s.opacity = opacity;
7803                 }
7804             }else{
7805                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7806             }
7807             return this;
7808         },
7809
7810         /**
7811          * Gets the left X coordinate
7812          * @param {Boolean} local True to get the local css position instead of page coordinate
7813          * @return {Number}
7814          */
7815         getLeft : function(local){
7816             if(!local){
7817                 return this.getX();
7818             }else{
7819                 return parseInt(this.getStyle("left"), 10) || 0;
7820             }
7821         },
7822
7823         /**
7824          * Gets the right X coordinate of the element (element X position + element width)
7825          * @param {Boolean} local True to get the local css position instead of page coordinate
7826          * @return {Number}
7827          */
7828         getRight : function(local){
7829             if(!local){
7830                 return this.getX() + this.getWidth();
7831             }else{
7832                 return (this.getLeft(true) + this.getWidth()) || 0;
7833             }
7834         },
7835
7836         /**
7837          * Gets the top Y coordinate
7838          * @param {Boolean} local True to get the local css position instead of page coordinate
7839          * @return {Number}
7840          */
7841         getTop : function(local) {
7842             if(!local){
7843                 return this.getY();
7844             }else{
7845                 return parseInt(this.getStyle("top"), 10) || 0;
7846             }
7847         },
7848
7849         /**
7850          * Gets the bottom Y coordinate of the element (element Y position + element height)
7851          * @param {Boolean} local True to get the local css position instead of page coordinate
7852          * @return {Number}
7853          */
7854         getBottom : function(local){
7855             if(!local){
7856                 return this.getY() + this.getHeight();
7857             }else{
7858                 return (this.getTop(true) + this.getHeight()) || 0;
7859             }
7860         },
7861
7862         /**
7863         * Initializes positioning on this element. If a desired position is not passed, it will make the
7864         * the element positioned relative IF it is not already positioned.
7865         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7866         * @param {Number} zIndex (optional) The zIndex to apply
7867         * @param {Number} x (optional) Set the page X position
7868         * @param {Number} y (optional) Set the page Y position
7869         */
7870         position : function(pos, zIndex, x, y){
7871             if(!pos){
7872                if(this.getStyle('position') == 'static'){
7873                    this.setStyle('position', 'relative');
7874                }
7875             }else{
7876                 this.setStyle("position", pos);
7877             }
7878             if(zIndex){
7879                 this.setStyle("z-index", zIndex);
7880             }
7881             if(x !== undefined && y !== undefined){
7882                 this.setXY([x, y]);
7883             }else if(x !== undefined){
7884                 this.setX(x);
7885             }else if(y !== undefined){
7886                 this.setY(y);
7887             }
7888         },
7889
7890         /**
7891         * Clear positioning back to the default when the document was loaded
7892         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7893         * @return {Roo.Element} this
7894          */
7895         clearPositioning : function(value){
7896             value = value ||'';
7897             this.setStyle({
7898                 "left": value,
7899                 "right": value,
7900                 "top": value,
7901                 "bottom": value,
7902                 "z-index": "",
7903                 "position" : "static"
7904             });
7905             return this;
7906         },
7907
7908         /**
7909         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7910         * snapshot before performing an update and then restoring the element.
7911         * @return {Object}
7912         */
7913         getPositioning : function(){
7914             var l = this.getStyle("left");
7915             var t = this.getStyle("top");
7916             return {
7917                 "position" : this.getStyle("position"),
7918                 "left" : l,
7919                 "right" : l ? "" : this.getStyle("right"),
7920                 "top" : t,
7921                 "bottom" : t ? "" : this.getStyle("bottom"),
7922                 "z-index" : this.getStyle("z-index")
7923             };
7924         },
7925
7926         /**
7927          * Gets the width of the border(s) for the specified side(s)
7928          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7929          * passing lr would get the border (l)eft width + the border (r)ight width.
7930          * @return {Number} The width of the sides passed added together
7931          */
7932         getBorderWidth : function(side){
7933             return this.addStyles(side, El.borders);
7934         },
7935
7936         /**
7937          * Gets the width of the padding(s) for the specified side(s)
7938          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7939          * passing lr would get the padding (l)eft + the padding (r)ight.
7940          * @return {Number} The padding of the sides passed added together
7941          */
7942         getPadding : function(side){
7943             return this.addStyles(side, El.paddings);
7944         },
7945
7946         /**
7947         * Set positioning with an object returned by getPositioning().
7948         * @param {Object} posCfg
7949         * @return {Roo.Element} this
7950          */
7951         setPositioning : function(pc){
7952             this.applyStyles(pc);
7953             if(pc.right == "auto"){
7954                 this.dom.style.right = "";
7955             }
7956             if(pc.bottom == "auto"){
7957                 this.dom.style.bottom = "";
7958             }
7959             return this;
7960         },
7961
7962         // private
7963         fixDisplay : function(){
7964             if(this.getStyle("display") == "none"){
7965                 this.setStyle("visibility", "hidden");
7966                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7967                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7968                     this.setStyle("display", "block");
7969                 }
7970             }
7971         },
7972
7973         /**
7974          * Quick set left and top adding default units
7975          * @param {String} left The left CSS property value
7976          * @param {String} top The top CSS property value
7977          * @return {Roo.Element} this
7978          */
7979          setLeftTop : function(left, top){
7980             this.dom.style.left = this.addUnits(left);
7981             this.dom.style.top = this.addUnits(top);
7982             return this;
7983         },
7984
7985         /**
7986          * Move this element relative to its current position.
7987          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7988          * @param {Number} distance How far to move the element in pixels
7989          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7990          * @return {Roo.Element} this
7991          */
7992          move : function(direction, distance, animate){
7993             var xy = this.getXY();
7994             direction = direction.toLowerCase();
7995             switch(direction){
7996                 case "l":
7997                 case "left":
7998                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7999                     break;
8000                case "r":
8001                case "right":
8002                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8003                     break;
8004                case "t":
8005                case "top":
8006                case "up":
8007                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8008                     break;
8009                case "b":
8010                case "bottom":
8011                case "down":
8012                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8013                     break;
8014             }
8015             return this;
8016         },
8017
8018         /**
8019          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8020          * @return {Roo.Element} this
8021          */
8022         clip : function(){
8023             if(!this.isClipped){
8024                this.isClipped = true;
8025                this.originalClip = {
8026                    "o": this.getStyle("overflow"),
8027                    "x": this.getStyle("overflow-x"),
8028                    "y": this.getStyle("overflow-y")
8029                };
8030                this.setStyle("overflow", "hidden");
8031                this.setStyle("overflow-x", "hidden");
8032                this.setStyle("overflow-y", "hidden");
8033             }
8034             return this;
8035         },
8036
8037         /**
8038          *  Return clipping (overflow) to original clipping before clip() was called
8039          * @return {Roo.Element} this
8040          */
8041         unclip : function(){
8042             if(this.isClipped){
8043                 this.isClipped = false;
8044                 var o = this.originalClip;
8045                 if(o.o){this.setStyle("overflow", o.o);}
8046                 if(o.x){this.setStyle("overflow-x", o.x);}
8047                 if(o.y){this.setStyle("overflow-y", o.y);}
8048             }
8049             return this;
8050         },
8051
8052
8053         /**
8054          * Gets the x,y coordinates specified by the anchor position on the element.
8055          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8056          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8057          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8058          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8059          * @return {Array} [x, y] An array containing the element's x and y coordinates
8060          */
8061         getAnchorXY : function(anchor, local, s){
8062             //Passing a different size is useful for pre-calculating anchors,
8063             //especially for anchored animations that change the el size.
8064
8065             var w, h, vp = false;
8066             if(!s){
8067                 var d = this.dom;
8068                 if(d == document.body || d == document){
8069                     vp = true;
8070                     w = D.getViewWidth(); h = D.getViewHeight();
8071                 }else{
8072                     w = this.getWidth(); h = this.getHeight();
8073                 }
8074             }else{
8075                 w = s.width;  h = s.height;
8076             }
8077             var x = 0, y = 0, r = Math.round;
8078             switch((anchor || "tl").toLowerCase()){
8079                 case "c":
8080                     x = r(w*.5);
8081                     y = r(h*.5);
8082                 break;
8083                 case "t":
8084                     x = r(w*.5);
8085                     y = 0;
8086                 break;
8087                 case "l":
8088                     x = 0;
8089                     y = r(h*.5);
8090                 break;
8091                 case "r":
8092                     x = w;
8093                     y = r(h*.5);
8094                 break;
8095                 case "b":
8096                     x = r(w*.5);
8097                     y = h;
8098                 break;
8099                 case "tl":
8100                     x = 0;
8101                     y = 0;
8102                 break;
8103                 case "bl":
8104                     x = 0;
8105                     y = h;
8106                 break;
8107                 case "br":
8108                     x = w;
8109                     y = h;
8110                 break;
8111                 case "tr":
8112                     x = w;
8113                     y = 0;
8114                 break;
8115             }
8116             if(local === true){
8117                 return [x, y];
8118             }
8119             if(vp){
8120                 var sc = this.getScroll();
8121                 return [x + sc.left, y + sc.top];
8122             }
8123             //Add the element's offset xy
8124             var o = this.getXY();
8125             return [x+o[0], y+o[1]];
8126         },
8127
8128         /**
8129          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8130          * supported position values.
8131          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8132          * @param {String} position The position to align to.
8133          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8134          * @return {Array} [x, y]
8135          */
8136         getAlignToXY : function(el, p, o){
8137             el = Roo.get(el);
8138             var d = this.dom;
8139             if(!el.dom){
8140                 throw "Element.alignTo with an element that doesn't exist";
8141             }
8142             var c = false; //constrain to viewport
8143             var p1 = "", p2 = "";
8144             o = o || [0,0];
8145
8146             if(!p){
8147                 p = "tl-bl";
8148             }else if(p == "?"){
8149                 p = "tl-bl?";
8150             }else if(p.indexOf("-") == -1){
8151                 p = "tl-" + p;
8152             }
8153             p = p.toLowerCase();
8154             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8155             if(!m){
8156                throw "Element.alignTo with an invalid alignment " + p;
8157             }
8158             p1 = m[1]; p2 = m[2]; c = !!m[3];
8159
8160             //Subtract the aligned el's internal xy from the target's offset xy
8161             //plus custom offset to get the aligned el's new offset xy
8162             var a1 = this.getAnchorXY(p1, true);
8163             var a2 = el.getAnchorXY(p2, false);
8164             var x = a2[0] - a1[0] + o[0];
8165             var y = a2[1] - a1[1] + o[1];
8166             if(c){
8167                 //constrain the aligned el to viewport if necessary
8168                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8169                 // 5px of margin for ie
8170                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8171
8172                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8173                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8174                 //otherwise swap the aligned el to the opposite border of the target.
8175                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8176                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8177                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8178                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8179
8180                var doc = document;
8181                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8182                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8183
8184                if((x+w) > dw + scrollX){
8185                     x = swapX ? r.left-w : dw+scrollX-w;
8186                 }
8187                if(x < scrollX){
8188                    x = swapX ? r.right : scrollX;
8189                }
8190                if((y+h) > dh + scrollY){
8191                     y = swapY ? r.top-h : dh+scrollY-h;
8192                 }
8193                if (y < scrollY){
8194                    y = swapY ? r.bottom : scrollY;
8195                }
8196             }
8197             return [x,y];
8198         },
8199
8200         // private
8201         getConstrainToXY : function(){
8202             var os = {top:0, left:0, bottom:0, right: 0};
8203
8204             return function(el, local, offsets, proposedXY){
8205                 el = Roo.get(el);
8206                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8207
8208                 var vw, vh, vx = 0, vy = 0;
8209                 if(el.dom == document.body || el.dom == document){
8210                     vw = Roo.lib.Dom.getViewWidth();
8211                     vh = Roo.lib.Dom.getViewHeight();
8212                 }else{
8213                     vw = el.dom.clientWidth;
8214                     vh = el.dom.clientHeight;
8215                     if(!local){
8216                         var vxy = el.getXY();
8217                         vx = vxy[0];
8218                         vy = vxy[1];
8219                     }
8220                 }
8221
8222                 var s = el.getScroll();
8223
8224                 vx += offsets.left + s.left;
8225                 vy += offsets.top + s.top;
8226
8227                 vw -= offsets.right;
8228                 vh -= offsets.bottom;
8229
8230                 var vr = vx+vw;
8231                 var vb = vy+vh;
8232
8233                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8234                 var x = xy[0], y = xy[1];
8235                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8236
8237                 // only move it if it needs it
8238                 var moved = false;
8239
8240                 // first validate right/bottom
8241                 if((x + w) > vr){
8242                     x = vr - w;
8243                     moved = true;
8244                 }
8245                 if((y + h) > vb){
8246                     y = vb - h;
8247                     moved = true;
8248                 }
8249                 // then make sure top/left isn't negative
8250                 if(x < vx){
8251                     x = vx;
8252                     moved = true;
8253                 }
8254                 if(y < vy){
8255                     y = vy;
8256                     moved = true;
8257                 }
8258                 return moved ? [x, y] : false;
8259             };
8260         }(),
8261
8262         // private
8263         adjustForConstraints : function(xy, parent, offsets){
8264             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8265         },
8266
8267         /**
8268          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8269          * document it aligns it to the viewport.
8270          * The position parameter is optional, and can be specified in any one of the following formats:
8271          * <ul>
8272          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8273          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8274          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8275          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8276          *   <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
8277          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8278          * </ul>
8279          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8280          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8281          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8282          * that specified in order to enforce the viewport constraints.
8283          * Following are all of the supported anchor positions:
8284     <pre>
8285     Value  Description
8286     -----  -----------------------------
8287     tl     The top left corner (default)
8288     t      The center of the top edge
8289     tr     The top right corner
8290     l      The center of the left edge
8291     c      In the center of the element
8292     r      The center of the right edge
8293     bl     The bottom left corner
8294     b      The center of the bottom edge
8295     br     The bottom right corner
8296     </pre>
8297     Example Usage:
8298     <pre><code>
8299     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8300     el.alignTo("other-el");
8301
8302     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8303     el.alignTo("other-el", "tr?");
8304
8305     // align the bottom right corner of el with the center left edge of other-el
8306     el.alignTo("other-el", "br-l?");
8307
8308     // align the center of el with the bottom left corner of other-el and
8309     // adjust the x position by -6 pixels (and the y position by 0)
8310     el.alignTo("other-el", "c-bl", [-6, 0]);
8311     </code></pre>
8312          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8313          * @param {String} position The position to align to.
8314          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8315          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8316          * @return {Roo.Element} this
8317          */
8318         alignTo : function(element, position, offsets, animate){
8319             var xy = this.getAlignToXY(element, position, offsets);
8320             this.setXY(xy, this.preanim(arguments, 3));
8321             return this;
8322         },
8323
8324         /**
8325          * Anchors an element to another element and realigns it when the window is resized.
8326          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8327          * @param {String} position The position to align to.
8328          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8329          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8330          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8331          * is a number, it is used as the buffer delay (defaults to 50ms).
8332          * @param {Function} callback The function to call after the animation finishes
8333          * @return {Roo.Element} this
8334          */
8335         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8336             var action = function(){
8337                 this.alignTo(el, alignment, offsets, animate);
8338                 Roo.callback(callback, this);
8339             };
8340             Roo.EventManager.onWindowResize(action, this);
8341             var tm = typeof monitorScroll;
8342             if(tm != 'undefined'){
8343                 Roo.EventManager.on(window, 'scroll', action, this,
8344                     {buffer: tm == 'number' ? monitorScroll : 50});
8345             }
8346             action.call(this); // align immediately
8347             return this;
8348         },
8349         /**
8350          * Clears any opacity settings from this element. Required in some cases for IE.
8351          * @return {Roo.Element} this
8352          */
8353         clearOpacity : function(){
8354             if (window.ActiveXObject) {
8355                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8356                     this.dom.style.filter = "";
8357                 }
8358             } else {
8359                 this.dom.style.opacity = "";
8360                 this.dom.style["-moz-opacity"] = "";
8361                 this.dom.style["-khtml-opacity"] = "";
8362             }
8363             return this;
8364         },
8365
8366         /**
8367          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8368          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8369          * @return {Roo.Element} this
8370          */
8371         hide : function(animate){
8372             this.setVisible(false, this.preanim(arguments, 0));
8373             return this;
8374         },
8375
8376         /**
8377         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8378         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8379          * @return {Roo.Element} this
8380          */
8381         show : function(animate){
8382             this.setVisible(true, this.preanim(arguments, 0));
8383             return this;
8384         },
8385
8386         /**
8387          * @private Test if size has a unit, otherwise appends the default
8388          */
8389         addUnits : function(size){
8390             return Roo.Element.addUnits(size, this.defaultUnit);
8391         },
8392
8393         /**
8394          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8395          * @return {Roo.Element} this
8396          */
8397         beginMeasure : function(){
8398             var el = this.dom;
8399             if(el.offsetWidth || el.offsetHeight){
8400                 return this; // offsets work already
8401             }
8402             var changed = [];
8403             var p = this.dom, b = document.body; // start with this element
8404             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8405                 var pe = Roo.get(p);
8406                 if(pe.getStyle('display') == 'none'){
8407                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8408                     p.style.visibility = "hidden";
8409                     p.style.display = "block";
8410                 }
8411                 p = p.parentNode;
8412             }
8413             this._measureChanged = changed;
8414             return this;
8415
8416         },
8417
8418         /**
8419          * Restores displays to before beginMeasure was called
8420          * @return {Roo.Element} this
8421          */
8422         endMeasure : function(){
8423             var changed = this._measureChanged;
8424             if(changed){
8425                 for(var i = 0, len = changed.length; i < len; i++) {
8426                     var r = changed[i];
8427                     r.el.style.visibility = r.visibility;
8428                     r.el.style.display = "none";
8429                 }
8430                 this._measureChanged = null;
8431             }
8432             return this;
8433         },
8434
8435         /**
8436         * Update the innerHTML of this element, optionally searching for and processing scripts
8437         * @param {String} html The new HTML
8438         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8439         * @param {Function} callback For async script loading you can be noticed when the update completes
8440         * @return {Roo.Element} this
8441          */
8442         update : function(html, loadScripts, callback){
8443             if(typeof html == "undefined"){
8444                 html = "";
8445             }
8446             if(loadScripts !== true){
8447                 this.dom.innerHTML = html;
8448                 if(typeof callback == "function"){
8449                     callback();
8450                 }
8451                 return this;
8452             }
8453             var id = Roo.id();
8454             var dom = this.dom;
8455
8456             html += '<span id="' + id + '"></span>';
8457
8458             E.onAvailable(id, function(){
8459                 var hd = document.getElementsByTagName("head")[0];
8460                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8461                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8462                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8463
8464                 var match;
8465                 while(match = re.exec(html)){
8466                     var attrs = match[1];
8467                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8468                     if(srcMatch && srcMatch[2]){
8469                        var s = document.createElement("script");
8470                        s.src = srcMatch[2];
8471                        var typeMatch = attrs.match(typeRe);
8472                        if(typeMatch && typeMatch[2]){
8473                            s.type = typeMatch[2];
8474                        }
8475                        hd.appendChild(s);
8476                     }else if(match[2] && match[2].length > 0){
8477                         if(window.execScript) {
8478                            window.execScript(match[2]);
8479                         } else {
8480                             /**
8481                              * eval:var:id
8482                              * eval:var:dom
8483                              * eval:var:html
8484                              * 
8485                              */
8486                            window.eval(match[2]);
8487                         }
8488                     }
8489                 }
8490                 var el = document.getElementById(id);
8491                 if(el){el.parentNode.removeChild(el);}
8492                 if(typeof callback == "function"){
8493                     callback();
8494                 }
8495             });
8496             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8497             return this;
8498         },
8499
8500         /**
8501          * Direct access to the UpdateManager update() method (takes the same parameters).
8502          * @param {String/Function} url The url for this request or a function to call to get the url
8503          * @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}
8504          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8505          * @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.
8506          * @return {Roo.Element} this
8507          */
8508         load : function(){
8509             var um = this.getUpdateManager();
8510             um.update.apply(um, arguments);
8511             return this;
8512         },
8513
8514         /**
8515         * Gets this element's UpdateManager
8516         * @return {Roo.UpdateManager} The UpdateManager
8517         */
8518         getUpdateManager : function(){
8519             if(!this.updateManager){
8520                 this.updateManager = new Roo.UpdateManager(this);
8521             }
8522             return this.updateManager;
8523         },
8524
8525         /**
8526          * Disables text selection for this element (normalized across browsers)
8527          * @return {Roo.Element} this
8528          */
8529         unselectable : function(){
8530             this.dom.unselectable = "on";
8531             this.swallowEvent("selectstart", true);
8532             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8533             this.addClass("x-unselectable");
8534             return this;
8535         },
8536
8537         /**
8538         * Calculates the x, y to center this element on the screen
8539         * @return {Array} The x, y values [x, y]
8540         */
8541         getCenterXY : function(){
8542             return this.getAlignToXY(document, 'c-c');
8543         },
8544
8545         /**
8546         * Centers the Element in either the viewport, or another Element.
8547         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8548         */
8549         center : function(centerIn){
8550             this.alignTo(centerIn || document, 'c-c');
8551             return this;
8552         },
8553
8554         /**
8555          * Tests various css rules/browsers to determine if this element uses a border box
8556          * @return {Boolean}
8557          */
8558         isBorderBox : function(){
8559             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8560         },
8561
8562         /**
8563          * Return a box {x, y, width, height} that can be used to set another elements
8564          * size/location to match this element.
8565          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8566          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8567          * @return {Object} box An object in the format {x, y, width, height}
8568          */
8569         getBox : function(contentBox, local){
8570             var xy;
8571             if(!local){
8572                 xy = this.getXY();
8573             }else{
8574                 var left = parseInt(this.getStyle("left"), 10) || 0;
8575                 var top = parseInt(this.getStyle("top"), 10) || 0;
8576                 xy = [left, top];
8577             }
8578             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8579             if(!contentBox){
8580                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8581             }else{
8582                 var l = this.getBorderWidth("l")+this.getPadding("l");
8583                 var r = this.getBorderWidth("r")+this.getPadding("r");
8584                 var t = this.getBorderWidth("t")+this.getPadding("t");
8585                 var b = this.getBorderWidth("b")+this.getPadding("b");
8586                 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)};
8587             }
8588             bx.right = bx.x + bx.width;
8589             bx.bottom = bx.y + bx.height;
8590             return bx;
8591         },
8592
8593         /**
8594          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8595          for more information about the sides.
8596          * @param {String} sides
8597          * @return {Number}
8598          */
8599         getFrameWidth : function(sides, onlyContentBox){
8600             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8601         },
8602
8603         /**
8604          * 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.
8605          * @param {Object} box The box to fill {x, y, width, height}
8606          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8607          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8608          * @return {Roo.Element} this
8609          */
8610         setBox : function(box, adjust, animate){
8611             var w = box.width, h = box.height;
8612             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8613                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8614                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8615             }
8616             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8617             return this;
8618         },
8619
8620         /**
8621          * Forces the browser to repaint this element
8622          * @return {Roo.Element} this
8623          */
8624          repaint : function(){
8625             var dom = this.dom;
8626             this.addClass("x-repaint");
8627             setTimeout(function(){
8628                 Roo.get(dom).removeClass("x-repaint");
8629             }, 1);
8630             return this;
8631         },
8632
8633         /**
8634          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8635          * then it returns the calculated width of the sides (see getPadding)
8636          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8637          * @return {Object/Number}
8638          */
8639         getMargins : function(side){
8640             if(!side){
8641                 return {
8642                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8643                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8644                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8645                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8646                 };
8647             }else{
8648                 return this.addStyles(side, El.margins);
8649              }
8650         },
8651
8652         // private
8653         addStyles : function(sides, styles){
8654             var val = 0, v, w;
8655             for(var i = 0, len = sides.length; i < len; i++){
8656                 v = this.getStyle(styles[sides.charAt(i)]);
8657                 if(v){
8658                      w = parseInt(v, 10);
8659                      if(w){ val += w; }
8660                 }
8661             }
8662             return val;
8663         },
8664
8665         /**
8666          * Creates a proxy element of this element
8667          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8668          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8669          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8670          * @return {Roo.Element} The new proxy element
8671          */
8672         createProxy : function(config, renderTo, matchBox){
8673             if(renderTo){
8674                 renderTo = Roo.getDom(renderTo);
8675             }else{
8676                 renderTo = document.body;
8677             }
8678             config = typeof config == "object" ?
8679                 config : {tag : "div", cls: config};
8680             var proxy = Roo.DomHelper.append(renderTo, config, true);
8681             if(matchBox){
8682                proxy.setBox(this.getBox());
8683             }
8684             return proxy;
8685         },
8686
8687         /**
8688          * Puts a mask over this element to disable user interaction. Requires core.css.
8689          * This method can only be applied to elements which accept child nodes.
8690          * @param {String} msg (optional) A message to display in the mask
8691          * @param {String} msgCls (optional) A css class to apply to the msg element
8692          * @return {Element} The mask  element
8693          */
8694         mask : function(msg, msgCls){
8695             if(this.getStyle("position") == "static"){
8696                 this.setStyle("position", "relative");
8697             }
8698             if(!this._mask){
8699                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8700             }
8701             this.addClass("x-masked");
8702             this._mask.setDisplayed(true);
8703             if(typeof msg == 'string'){
8704                 if(!this._maskMsg){
8705                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8706                 }
8707                 var mm = this._maskMsg;
8708                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8709                 mm.dom.firstChild.innerHTML = msg;
8710                 mm.setDisplayed(true);
8711                 mm.center(this);
8712             }
8713             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8714                 this._mask.setHeight(this.getHeight());
8715             }
8716             return this._mask;
8717         },
8718
8719         /**
8720          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8721          * it is cached for reuse.
8722          */
8723         unmask : function(removeEl){
8724             if(this._mask){
8725                 if(removeEl === true){
8726                     this._mask.remove();
8727                     delete this._mask;
8728                     if(this._maskMsg){
8729                         this._maskMsg.remove();
8730                         delete this._maskMsg;
8731                     }
8732                 }else{
8733                     this._mask.setDisplayed(false);
8734                     if(this._maskMsg){
8735                         this._maskMsg.setDisplayed(false);
8736                     }
8737                 }
8738             }
8739             this.removeClass("x-masked");
8740         },
8741
8742         /**
8743          * Returns true if this element is masked
8744          * @return {Boolean}
8745          */
8746         isMasked : function(){
8747             return this._mask && this._mask.isVisible();
8748         },
8749
8750         /**
8751          * Creates an iframe shim for this element to keep selects and other windowed objects from
8752          * showing through.
8753          * @return {Roo.Element} The new shim element
8754          */
8755         createShim : function(){
8756             var el = document.createElement('iframe');
8757             el.frameBorder = 'no';
8758             el.className = 'roo-shim';
8759             if(Roo.isIE && Roo.isSecure){
8760                 el.src = Roo.SSL_SECURE_URL;
8761             }
8762             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8763             shim.autoBoxAdjust = false;
8764             return shim;
8765         },
8766
8767         /**
8768          * Removes this element from the DOM and deletes it from the cache
8769          */
8770         remove : function(){
8771             if(this.dom.parentNode){
8772                 this.dom.parentNode.removeChild(this.dom);
8773             }
8774             delete El.cache[this.dom.id];
8775         },
8776
8777         /**
8778          * Sets up event handlers to add and remove a css class when the mouse is over this element
8779          * @param {String} className
8780          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8781          * mouseout events for children elements
8782          * @return {Roo.Element} this
8783          */
8784         addClassOnOver : function(className, preventFlicker){
8785             this.on("mouseover", function(){
8786                 Roo.fly(this, '_internal').addClass(className);
8787             }, this.dom);
8788             var removeFn = function(e){
8789                 if(preventFlicker !== true || !e.within(this, true)){
8790                     Roo.fly(this, '_internal').removeClass(className);
8791                 }
8792             };
8793             this.on("mouseout", removeFn, this.dom);
8794             return this;
8795         },
8796
8797         /**
8798          * Sets up event handlers to add and remove a css class when this element has the focus
8799          * @param {String} className
8800          * @return {Roo.Element} this
8801          */
8802         addClassOnFocus : function(className){
8803             this.on("focus", function(){
8804                 Roo.fly(this, '_internal').addClass(className);
8805             }, this.dom);
8806             this.on("blur", function(){
8807                 Roo.fly(this, '_internal').removeClass(className);
8808             }, this.dom);
8809             return this;
8810         },
8811         /**
8812          * 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)
8813          * @param {String} className
8814          * @return {Roo.Element} this
8815          */
8816         addClassOnClick : function(className){
8817             var dom = this.dom;
8818             this.on("mousedown", function(){
8819                 Roo.fly(dom, '_internal').addClass(className);
8820                 var d = Roo.get(document);
8821                 var fn = function(){
8822                     Roo.fly(dom, '_internal').removeClass(className);
8823                     d.removeListener("mouseup", fn);
8824                 };
8825                 d.on("mouseup", fn);
8826             });
8827             return this;
8828         },
8829
8830         /**
8831          * Stops the specified event from bubbling and optionally prevents the default action
8832          * @param {String} eventName
8833          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8834          * @return {Roo.Element} this
8835          */
8836         swallowEvent : function(eventName, preventDefault){
8837             var fn = function(e){
8838                 e.stopPropagation();
8839                 if(preventDefault){
8840                     e.preventDefault();
8841                 }
8842             };
8843             if(eventName instanceof Array){
8844                 for(var i = 0, len = eventName.length; i < len; i++){
8845                      this.on(eventName[i], fn);
8846                 }
8847                 return this;
8848             }
8849             this.on(eventName, fn);
8850             return this;
8851         },
8852
8853         /**
8854          * @private
8855          */
8856       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8857
8858         /**
8859          * Sizes this element to its parent element's dimensions performing
8860          * neccessary box adjustments.
8861          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8862          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8863          * @return {Roo.Element} this
8864          */
8865         fitToParent : function(monitorResize, targetParent) {
8866           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8867           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8868           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8869             return;
8870           }
8871           var p = Roo.get(targetParent || this.dom.parentNode);
8872           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8873           if (monitorResize === true) {
8874             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8875             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8876           }
8877           return this;
8878         },
8879
8880         /**
8881          * Gets the next sibling, skipping text nodes
8882          * @return {HTMLElement} The next sibling or null
8883          */
8884         getNextSibling : function(){
8885             var n = this.dom.nextSibling;
8886             while(n && n.nodeType != 1){
8887                 n = n.nextSibling;
8888             }
8889             return n;
8890         },
8891
8892         /**
8893          * Gets the previous sibling, skipping text nodes
8894          * @return {HTMLElement} The previous sibling or null
8895          */
8896         getPrevSibling : function(){
8897             var n = this.dom.previousSibling;
8898             while(n && n.nodeType != 1){
8899                 n = n.previousSibling;
8900             }
8901             return n;
8902         },
8903
8904
8905         /**
8906          * Appends the passed element(s) to this element
8907          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8908          * @return {Roo.Element} this
8909          */
8910         appendChild: function(el){
8911             el = Roo.get(el);
8912             el.appendTo(this);
8913             return this;
8914         },
8915
8916         /**
8917          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8918          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8919          * automatically generated with the specified attributes.
8920          * @param {HTMLElement} insertBefore (optional) a child element of this element
8921          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8922          * @return {Roo.Element} The new child element
8923          */
8924         createChild: function(config, insertBefore, returnDom){
8925             config = config || {tag:'div'};
8926             if(insertBefore){
8927                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8928             }
8929             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8930         },
8931
8932         /**
8933          * Appends this element to the passed element
8934          * @param {String/HTMLElement/Element} el The new parent element
8935          * @return {Roo.Element} this
8936          */
8937         appendTo: function(el){
8938             el = Roo.getDom(el);
8939             el.appendChild(this.dom);
8940             return this;
8941         },
8942
8943         /**
8944          * Inserts this element before the passed element in the DOM
8945          * @param {String/HTMLElement/Element} el The element to insert before
8946          * @return {Roo.Element} this
8947          */
8948         insertBefore: function(el){
8949             el = Roo.getDom(el);
8950             el.parentNode.insertBefore(this.dom, el);
8951             return this;
8952         },
8953
8954         /**
8955          * Inserts this element after the passed element in the DOM
8956          * @param {String/HTMLElement/Element} el The element to insert after
8957          * @return {Roo.Element} this
8958          */
8959         insertAfter: function(el){
8960             el = Roo.getDom(el);
8961             el.parentNode.insertBefore(this.dom, el.nextSibling);
8962             return this;
8963         },
8964
8965         /**
8966          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8967          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8968          * @return {Roo.Element} The new child
8969          */
8970         insertFirst: function(el, returnDom){
8971             el = el || {};
8972             if(typeof el == 'object' && !el.nodeType){ // dh config
8973                 return this.createChild(el, this.dom.firstChild, returnDom);
8974             }else{
8975                 el = Roo.getDom(el);
8976                 this.dom.insertBefore(el, this.dom.firstChild);
8977                 return !returnDom ? Roo.get(el) : el;
8978             }
8979         },
8980
8981         /**
8982          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8983          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8984          * @param {String} where (optional) 'before' or 'after' defaults to before
8985          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8986          * @return {Roo.Element} the inserted Element
8987          */
8988         insertSibling: function(el, where, returnDom){
8989             where = where ? where.toLowerCase() : 'before';
8990             el = el || {};
8991             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8992
8993             if(typeof el == 'object' && !el.nodeType){ // dh config
8994                 if(where == 'after' && !this.dom.nextSibling){
8995                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8996                 }else{
8997                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8998                 }
8999
9000             }else{
9001                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9002                             where == 'before' ? this.dom : this.dom.nextSibling);
9003                 if(!returnDom){
9004                     rt = Roo.get(rt);
9005                 }
9006             }
9007             return rt;
9008         },
9009
9010         /**
9011          * Creates and wraps this element with another element
9012          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9013          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9014          * @return {HTMLElement/Element} The newly created wrapper element
9015          */
9016         wrap: function(config, returnDom){
9017             if(!config){
9018                 config = {tag: "div"};
9019             }
9020             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9021             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9022             return newEl;
9023         },
9024
9025         /**
9026          * Replaces the passed element with this element
9027          * @param {String/HTMLElement/Element} el The element to replace
9028          * @return {Roo.Element} this
9029          */
9030         replace: function(el){
9031             el = Roo.get(el);
9032             this.insertBefore(el);
9033             el.remove();
9034             return this;
9035         },
9036
9037         /**
9038          * Inserts an html fragment into this element
9039          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9040          * @param {String} html The HTML fragment
9041          * @param {Boolean} returnEl True to return an Roo.Element
9042          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9043          */
9044         insertHtml : function(where, html, returnEl){
9045             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9046             return returnEl ? Roo.get(el) : el;
9047         },
9048
9049         /**
9050          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9051          * @param {Object} o The object with the attributes
9052          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9053          * @return {Roo.Element} this
9054          */
9055         set : function(o, useSet){
9056             var el = this.dom;
9057             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9058             for(var attr in o){
9059                 if(attr == "style" || typeof o[attr] == "function") continue;
9060                 if(attr=="cls"){
9061                     el.className = o["cls"];
9062                 }else{
9063                     if(useSet) el.setAttribute(attr, o[attr]);
9064                     else el[attr] = o[attr];
9065                 }
9066             }
9067             if(o.style){
9068                 Roo.DomHelper.applyStyles(el, o.style);
9069             }
9070             return this;
9071         },
9072
9073         /**
9074          * Convenience method for constructing a KeyMap
9075          * @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:
9076          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9077          * @param {Function} fn The function to call
9078          * @param {Object} scope (optional) The scope of the function
9079          * @return {Roo.KeyMap} The KeyMap created
9080          */
9081         addKeyListener : function(key, fn, scope){
9082             var config;
9083             if(typeof key != "object" || key instanceof Array){
9084                 config = {
9085                     key: key,
9086                     fn: fn,
9087                     scope: scope
9088                 };
9089             }else{
9090                 config = {
9091                     key : key.key,
9092                     shift : key.shift,
9093                     ctrl : key.ctrl,
9094                     alt : key.alt,
9095                     fn: fn,
9096                     scope: scope
9097                 };
9098             }
9099             return new Roo.KeyMap(this, config);
9100         },
9101
9102         /**
9103          * Creates a KeyMap for this element
9104          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9105          * @return {Roo.KeyMap} The KeyMap created
9106          */
9107         addKeyMap : function(config){
9108             return new Roo.KeyMap(this, config);
9109         },
9110
9111         /**
9112          * Returns true if this element is scrollable.
9113          * @return {Boolean}
9114          */
9115          isScrollable : function(){
9116             var dom = this.dom;
9117             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9118         },
9119
9120         /**
9121          * 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().
9122          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9123          * @param {Number} value The new scroll value
9124          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9125          * @return {Element} this
9126          */
9127
9128         scrollTo : function(side, value, animate){
9129             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9130             if(!animate || !A){
9131                 this.dom[prop] = value;
9132             }else{
9133                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9134                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9135             }
9136             return this;
9137         },
9138
9139         /**
9140          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9141          * within this element's scrollable range.
9142          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9143          * @param {Number} distance How far to scroll the element in pixels
9144          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9145          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9146          * was scrolled as far as it could go.
9147          */
9148          scroll : function(direction, distance, animate){
9149              if(!this.isScrollable()){
9150                  return;
9151              }
9152              var el = this.dom;
9153              var l = el.scrollLeft, t = el.scrollTop;
9154              var w = el.scrollWidth, h = el.scrollHeight;
9155              var cw = el.clientWidth, ch = el.clientHeight;
9156              direction = direction.toLowerCase();
9157              var scrolled = false;
9158              var a = this.preanim(arguments, 2);
9159              switch(direction){
9160                  case "l":
9161                  case "left":
9162                      if(w - l > cw){
9163                          var v = Math.min(l + distance, w-cw);
9164                          this.scrollTo("left", v, a);
9165                          scrolled = true;
9166                      }
9167                      break;
9168                 case "r":
9169                 case "right":
9170                      if(l > 0){
9171                          var v = Math.max(l - distance, 0);
9172                          this.scrollTo("left", v, a);
9173                          scrolled = true;
9174                      }
9175                      break;
9176                 case "t":
9177                 case "top":
9178                 case "up":
9179                      if(t > 0){
9180                          var v = Math.max(t - distance, 0);
9181                          this.scrollTo("top", v, a);
9182                          scrolled = true;
9183                      }
9184                      break;
9185                 case "b":
9186                 case "bottom":
9187                 case "down":
9188                      if(h - t > ch){
9189                          var v = Math.min(t + distance, h-ch);
9190                          this.scrollTo("top", v, a);
9191                          scrolled = true;
9192                      }
9193                      break;
9194              }
9195              return scrolled;
9196         },
9197
9198         /**
9199          * Translates the passed page coordinates into left/top css values for this element
9200          * @param {Number/Array} x The page x or an array containing [x, y]
9201          * @param {Number} y The page y
9202          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9203          */
9204         translatePoints : function(x, y){
9205             if(typeof x == 'object' || x instanceof Array){
9206                 y = x[1]; x = x[0];
9207             }
9208             var p = this.getStyle('position');
9209             var o = this.getXY();
9210
9211             var l = parseInt(this.getStyle('left'), 10);
9212             var t = parseInt(this.getStyle('top'), 10);
9213
9214             if(isNaN(l)){
9215                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9216             }
9217             if(isNaN(t)){
9218                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9219             }
9220
9221             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9222         },
9223
9224         /**
9225          * Returns the current scroll position of the element.
9226          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9227          */
9228         getScroll : function(){
9229             var d = this.dom, doc = document;
9230             if(d == doc || d == doc.body){
9231                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9232                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9233                 return {left: l, top: t};
9234             }else{
9235                 return {left: d.scrollLeft, top: d.scrollTop};
9236             }
9237         },
9238
9239         /**
9240          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9241          * are convert to standard 6 digit hex color.
9242          * @param {String} attr The css attribute
9243          * @param {String} defaultValue The default value to use when a valid color isn't found
9244          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9245          * YUI color anims.
9246          */
9247         getColor : function(attr, defaultValue, prefix){
9248             var v = this.getStyle(attr);
9249             if(!v || v == "transparent" || v == "inherit") {
9250                 return defaultValue;
9251             }
9252             var color = typeof prefix == "undefined" ? "#" : prefix;
9253             if(v.substr(0, 4) == "rgb("){
9254                 var rvs = v.slice(4, v.length -1).split(",");
9255                 for(var i = 0; i < 3; i++){
9256                     var h = parseInt(rvs[i]).toString(16);
9257                     if(h < 16){
9258                         h = "0" + h;
9259                     }
9260                     color += h;
9261                 }
9262             } else {
9263                 if(v.substr(0, 1) == "#"){
9264                     if(v.length == 4) {
9265                         for(var i = 1; i < 4; i++){
9266                             var c = v.charAt(i);
9267                             color +=  c + c;
9268                         }
9269                     }else if(v.length == 7){
9270                         color += v.substr(1);
9271                     }
9272                 }
9273             }
9274             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9275         },
9276
9277         /**
9278          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9279          * gradient background, rounded corners and a 4-way shadow.
9280          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9281          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9282          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9283          * @return {Roo.Element} this
9284          */
9285         boxWrap : function(cls){
9286             cls = cls || 'x-box';
9287             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9288             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9289             return el;
9290         },
9291
9292         /**
9293          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9294          * @param {String} namespace The namespace in which to look for the attribute
9295          * @param {String} name The attribute name
9296          * @return {String} The attribute value
9297          */
9298         getAttributeNS : Roo.isIE ? function(ns, name){
9299             var d = this.dom;
9300             var type = typeof d[ns+":"+name];
9301             if(type != 'undefined' && type != 'unknown'){
9302                 return d[ns+":"+name];
9303             }
9304             return d[name];
9305         } : function(ns, name){
9306             var d = this.dom;
9307             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9308         }
9309     };
9310
9311     var ep = El.prototype;
9312
9313     /**
9314      * Appends an event handler (Shorthand for addListener)
9315      * @param {String}   eventName     The type of event to append
9316      * @param {Function} fn        The method the event invokes
9317      * @param {Object} scope       (optional) The scope (this object) of the fn
9318      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9319      * @method
9320      */
9321     ep.on = ep.addListener;
9322         // backwards compat
9323     ep.mon = ep.addListener;
9324
9325     /**
9326      * Removes an event handler from this element (shorthand for removeListener)
9327      * @param {String} eventName the type of event to remove
9328      * @param {Function} fn the method the event invokes
9329      * @return {Roo.Element} this
9330      * @method
9331      */
9332     ep.un = ep.removeListener;
9333
9334     /**
9335      * true to automatically adjust width and height settings for box-model issues (default to true)
9336      */
9337     ep.autoBoxAdjust = true;
9338
9339     // private
9340     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9341
9342     // private
9343     El.addUnits = function(v, defaultUnit){
9344         if(v === "" || v == "auto"){
9345             return v;
9346         }
9347         if(v === undefined){
9348             return '';
9349         }
9350         if(typeof v == "number" || !El.unitPattern.test(v)){
9351             return v + (defaultUnit || 'px');
9352         }
9353         return v;
9354     };
9355
9356     // special markup used throughout Roo when box wrapping elements
9357     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>';
9358     /**
9359      * Visibility mode constant - Use visibility to hide element
9360      * @static
9361      * @type Number
9362      */
9363     El.VISIBILITY = 1;
9364     /**
9365      * Visibility mode constant - Use display to hide element
9366      * @static
9367      * @type Number
9368      */
9369     El.DISPLAY = 2;
9370
9371     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9372     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9373     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9374
9375
9376
9377     /**
9378      * @private
9379      */
9380     El.cache = {};
9381
9382     var docEl;
9383
9384     /**
9385      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9386      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9387      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9388      * @return {Element} The Element object
9389      * @static
9390      */
9391     El.get = function(el){
9392         var ex, elm, id;
9393         if(!el){ return null; }
9394         if(typeof el == "string"){ // element id
9395             if(!(elm = document.getElementById(el))){
9396                 return null;
9397             }
9398             if(ex = El.cache[el]){
9399                 ex.dom = elm;
9400             }else{
9401                 ex = El.cache[el] = new El(elm);
9402             }
9403             return ex;
9404         }else if(el.tagName){ // dom element
9405             if(!(id = el.id)){
9406                 id = Roo.id(el);
9407             }
9408             if(ex = El.cache[id]){
9409                 ex.dom = el;
9410             }else{
9411                 ex = El.cache[id] = new El(el);
9412             }
9413             return ex;
9414         }else if(el instanceof El){
9415             if(el != docEl){
9416                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9417                                                               // catch case where it hasn't been appended
9418                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9419             }
9420             return el;
9421         }else if(el.isComposite){
9422             return el;
9423         }else if(el instanceof Array){
9424             return El.select(el);
9425         }else if(el == document){
9426             // create a bogus element object representing the document object
9427             if(!docEl){
9428                 var f = function(){};
9429                 f.prototype = El.prototype;
9430                 docEl = new f();
9431                 docEl.dom = document;
9432             }
9433             return docEl;
9434         }
9435         return null;
9436     };
9437
9438     // private
9439     El.uncache = function(el){
9440         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9441             if(a[i]){
9442                 delete El.cache[a[i].id || a[i]];
9443             }
9444         }
9445     };
9446
9447     // private
9448     // Garbage collection - uncache elements/purge listeners on orphaned elements
9449     // so we don't hold a reference and cause the browser to retain them
9450     El.garbageCollect = function(){
9451         if(!Roo.enableGarbageCollector){
9452             clearInterval(El.collectorThread);
9453             return;
9454         }
9455         for(var eid in El.cache){
9456             var el = El.cache[eid], d = el.dom;
9457             // -------------------------------------------------------
9458             // Determining what is garbage:
9459             // -------------------------------------------------------
9460             // !d
9461             // dom node is null, definitely garbage
9462             // -------------------------------------------------------
9463             // !d.parentNode
9464             // no parentNode == direct orphan, definitely garbage
9465             // -------------------------------------------------------
9466             // !d.offsetParent && !document.getElementById(eid)
9467             // display none elements have no offsetParent so we will
9468             // also try to look it up by it's id. However, check
9469             // offsetParent first so we don't do unneeded lookups.
9470             // This enables collection of elements that are not orphans
9471             // directly, but somewhere up the line they have an orphan
9472             // parent.
9473             // -------------------------------------------------------
9474             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9475                 delete El.cache[eid];
9476                 if(d && Roo.enableListenerCollection){
9477                     E.purgeElement(d);
9478                 }
9479             }
9480         }
9481     }
9482     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9483
9484
9485     // dom is optional
9486     El.Flyweight = function(dom){
9487         this.dom = dom;
9488     };
9489     El.Flyweight.prototype = El.prototype;
9490
9491     El._flyweights = {};
9492     /**
9493      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9494      * the dom node can be overwritten by other code.
9495      * @param {String/HTMLElement} el The dom node or id
9496      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9497      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9498      * @static
9499      * @return {Element} The shared Element object
9500      */
9501     El.fly = function(el, named){
9502         named = named || '_global';
9503         el = Roo.getDom(el);
9504         if(!el){
9505             return null;
9506         }
9507         if(!El._flyweights[named]){
9508             El._flyweights[named] = new El.Flyweight();
9509         }
9510         El._flyweights[named].dom = el;
9511         return El._flyweights[named];
9512     };
9513
9514     /**
9515      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9516      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9517      * Shorthand of {@link Roo.Element#get}
9518      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9519      * @return {Element} The Element object
9520      * @member Roo
9521      * @method get
9522      */
9523     Roo.get = El.get;
9524     /**
9525      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9526      * the dom node can be overwritten by other code.
9527      * Shorthand of {@link Roo.Element#fly}
9528      * @param {String/HTMLElement} el The dom node or id
9529      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9530      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9531      * @static
9532      * @return {Element} The shared Element object
9533      * @member Roo
9534      * @method fly
9535      */
9536     Roo.fly = El.fly;
9537
9538     // speedy lookup for elements never to box adjust
9539     var noBoxAdjust = Roo.isStrict ? {
9540         select:1
9541     } : {
9542         input:1, select:1, textarea:1
9543     };
9544     if(Roo.isIE || Roo.isGecko){
9545         noBoxAdjust['button'] = 1;
9546     }
9547
9548
9549     Roo.EventManager.on(window, 'unload', function(){
9550         delete El.cache;
9551         delete El._flyweights;
9552     });
9553 })();
9554
9555
9556
9557
9558 if(Roo.DomQuery){
9559     Roo.Element.selectorFunction = Roo.DomQuery.select;
9560 }
9561
9562 Roo.Element.select = function(selector, unique, root){
9563     var els;
9564     if(typeof selector == "string"){
9565         els = Roo.Element.selectorFunction(selector, root);
9566     }else if(selector.length !== undefined){
9567         els = selector;
9568     }else{
9569         throw "Invalid selector";
9570     }
9571     if(unique === true){
9572         return new Roo.CompositeElement(els);
9573     }else{
9574         return new Roo.CompositeElementLite(els);
9575     }
9576 };
9577 /**
9578  * Selects elements based on the passed CSS selector to enable working on them as 1.
9579  * @param {String/Array} selector The CSS selector or an array of elements
9580  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9581  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9582  * @return {CompositeElementLite/CompositeElement}
9583  * @member Roo
9584  * @method select
9585  */
9586 Roo.select = Roo.Element.select;
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601 /*
9602  * Based on:
9603  * Ext JS Library 1.1.1
9604  * Copyright(c) 2006-2007, Ext JS, LLC.
9605  *
9606  * Originally Released Under LGPL - original licence link has changed is not relivant.
9607  *
9608  * Fork - LGPL
9609  * <script type="text/javascript">
9610  */
9611
9612
9613
9614 //Notifies Element that fx methods are available
9615 Roo.enableFx = true;
9616
9617 /**
9618  * @class Roo.Fx
9619  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9620  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9621  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9622  * Element effects to work.</p><br/>
9623  *
9624  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9625  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9626  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9627  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9628  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9629  * expected results and should be done with care.</p><br/>
9630  *
9631  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9632  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9633 <pre>
9634 Value  Description
9635 -----  -----------------------------
9636 tl     The top left corner
9637 t      The center of the top edge
9638 tr     The top right corner
9639 l      The center of the left edge
9640 r      The center of the right edge
9641 bl     The bottom left corner
9642 b      The center of the bottom edge
9643 br     The bottom right corner
9644 </pre>
9645  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9646  * below are common options that can be passed to any Fx method.</b>
9647  * @cfg {Function} callback A function called when the effect is finished
9648  * @cfg {Object} scope The scope of the effect function
9649  * @cfg {String} easing A valid Easing value for the effect
9650  * @cfg {String} afterCls A css class to apply after the effect
9651  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9652  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9653  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9654  * effects that end with the element being visually hidden, ignored otherwise)
9655  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9656  * a function which returns such a specification that will be applied to the Element after the effect finishes
9657  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9658  * @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
9659  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9660  */
9661 Roo.Fx = {
9662         /**
9663          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9664          * origin for the slide effect.  This function automatically handles wrapping the element with
9665          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9666          * Usage:
9667          *<pre><code>
9668 // default: slide the element in from the top
9669 el.slideIn();
9670
9671 // custom: slide the element in from the right with a 2-second duration
9672 el.slideIn('r', { duration: 2 });
9673
9674 // common config options shown with default values
9675 el.slideIn('t', {
9676     easing: 'easeOut',
9677     duration: .5
9678 });
9679 </code></pre>
9680          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9681          * @param {Object} options (optional) Object literal with any of the Fx config options
9682          * @return {Roo.Element} The Element
9683          */
9684     slideIn : function(anchor, o){
9685         var el = this.getFxEl();
9686         o = o || {};
9687
9688         el.queueFx(o, function(){
9689
9690             anchor = anchor || "t";
9691
9692             // fix display to visibility
9693             this.fixDisplay();
9694
9695             // restore values after effect
9696             var r = this.getFxRestore();
9697             var b = this.getBox();
9698             // fixed size for slide
9699             this.setSize(b);
9700
9701             // wrap if needed
9702             var wrap = this.fxWrap(r.pos, o, "hidden");
9703
9704             var st = this.dom.style;
9705             st.visibility = "visible";
9706             st.position = "absolute";
9707
9708             // clear out temp styles after slide and unwrap
9709             var after = function(){
9710                 el.fxUnwrap(wrap, r.pos, o);
9711                 st.width = r.width;
9712                 st.height = r.height;
9713                 el.afterFx(o);
9714             };
9715             // time to calc the positions
9716             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9717
9718             switch(anchor.toLowerCase()){
9719                 case "t":
9720                     wrap.setSize(b.width, 0);
9721                     st.left = st.bottom = "0";
9722                     a = {height: bh};
9723                 break;
9724                 case "l":
9725                     wrap.setSize(0, b.height);
9726                     st.right = st.top = "0";
9727                     a = {width: bw};
9728                 break;
9729                 case "r":
9730                     wrap.setSize(0, b.height);
9731                     wrap.setX(b.right);
9732                     st.left = st.top = "0";
9733                     a = {width: bw, points: pt};
9734                 break;
9735                 case "b":
9736                     wrap.setSize(b.width, 0);
9737                     wrap.setY(b.bottom);
9738                     st.left = st.top = "0";
9739                     a = {height: bh, points: pt};
9740                 break;
9741                 case "tl":
9742                     wrap.setSize(0, 0);
9743                     st.right = st.bottom = "0";
9744                     a = {width: bw, height: bh};
9745                 break;
9746                 case "bl":
9747                     wrap.setSize(0, 0);
9748                     wrap.setY(b.y+b.height);
9749                     st.right = st.top = "0";
9750                     a = {width: bw, height: bh, points: pt};
9751                 break;
9752                 case "br":
9753                     wrap.setSize(0, 0);
9754                     wrap.setXY([b.right, b.bottom]);
9755                     st.left = st.top = "0";
9756                     a = {width: bw, height: bh, points: pt};
9757                 break;
9758                 case "tr":
9759                     wrap.setSize(0, 0);
9760                     wrap.setX(b.x+b.width);
9761                     st.left = st.bottom = "0";
9762                     a = {width: bw, height: bh, points: pt};
9763                 break;
9764             }
9765             this.dom.style.visibility = "visible";
9766             wrap.show();
9767
9768             arguments.callee.anim = wrap.fxanim(a,
9769                 o,
9770                 'motion',
9771                 .5,
9772                 'easeOut', after);
9773         });
9774         return this;
9775     },
9776     
9777         /**
9778          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9779          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9780          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9781          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9782          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9783          * Usage:
9784          *<pre><code>
9785 // default: slide the element out to the top
9786 el.slideOut();
9787
9788 // custom: slide the element out to the right with a 2-second duration
9789 el.slideOut('r', { duration: 2 });
9790
9791 // common config options shown with default values
9792 el.slideOut('t', {
9793     easing: 'easeOut',
9794     duration: .5,
9795     remove: false,
9796     useDisplay: false
9797 });
9798 </code></pre>
9799          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9800          * @param {Object} options (optional) Object literal with any of the Fx config options
9801          * @return {Roo.Element} The Element
9802          */
9803     slideOut : function(anchor, o){
9804         var el = this.getFxEl();
9805         o = o || {};
9806
9807         el.queueFx(o, function(){
9808
9809             anchor = anchor || "t";
9810
9811             // restore values after effect
9812             var r = this.getFxRestore();
9813             
9814             var b = this.getBox();
9815             // fixed size for slide
9816             this.setSize(b);
9817
9818             // wrap if needed
9819             var wrap = this.fxWrap(r.pos, o, "visible");
9820
9821             var st = this.dom.style;
9822             st.visibility = "visible";
9823             st.position = "absolute";
9824
9825             wrap.setSize(b);
9826
9827             var after = function(){
9828                 if(o.useDisplay){
9829                     el.setDisplayed(false);
9830                 }else{
9831                     el.hide();
9832                 }
9833
9834                 el.fxUnwrap(wrap, r.pos, o);
9835
9836                 st.width = r.width;
9837                 st.height = r.height;
9838
9839                 el.afterFx(o);
9840             };
9841
9842             var a, zero = {to: 0};
9843             switch(anchor.toLowerCase()){
9844                 case "t":
9845                     st.left = st.bottom = "0";
9846                     a = {height: zero};
9847                 break;
9848                 case "l":
9849                     st.right = st.top = "0";
9850                     a = {width: zero};
9851                 break;
9852                 case "r":
9853                     st.left = st.top = "0";
9854                     a = {width: zero, points: {to:[b.right, b.y]}};
9855                 break;
9856                 case "b":
9857                     st.left = st.top = "0";
9858                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9859                 break;
9860                 case "tl":
9861                     st.right = st.bottom = "0";
9862                     a = {width: zero, height: zero};
9863                 break;
9864                 case "bl":
9865                     st.right = st.top = "0";
9866                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9867                 break;
9868                 case "br":
9869                     st.left = st.top = "0";
9870                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9871                 break;
9872                 case "tr":
9873                     st.left = st.bottom = "0";
9874                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9875                 break;
9876             }
9877
9878             arguments.callee.anim = wrap.fxanim(a,
9879                 o,
9880                 'motion',
9881                 .5,
9882                 "easeOut", after);
9883         });
9884         return this;
9885     },
9886
9887         /**
9888          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9889          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9890          * The element must be removed from the DOM using the 'remove' config option if desired.
9891          * Usage:
9892          *<pre><code>
9893 // default
9894 el.puff();
9895
9896 // common config options shown with default values
9897 el.puff({
9898     easing: 'easeOut',
9899     duration: .5,
9900     remove: false,
9901     useDisplay: false
9902 });
9903 </code></pre>
9904          * @param {Object} options (optional) Object literal with any of the Fx config options
9905          * @return {Roo.Element} The Element
9906          */
9907     puff : function(o){
9908         var el = this.getFxEl();
9909         o = o || {};
9910
9911         el.queueFx(o, function(){
9912             this.clearOpacity();
9913             this.show();
9914
9915             // restore values after effect
9916             var r = this.getFxRestore();
9917             var st = this.dom.style;
9918
9919             var after = function(){
9920                 if(o.useDisplay){
9921                     el.setDisplayed(false);
9922                 }else{
9923                     el.hide();
9924                 }
9925
9926                 el.clearOpacity();
9927
9928                 el.setPositioning(r.pos);
9929                 st.width = r.width;
9930                 st.height = r.height;
9931                 st.fontSize = '';
9932                 el.afterFx(o);
9933             };
9934
9935             var width = this.getWidth();
9936             var height = this.getHeight();
9937
9938             arguments.callee.anim = this.fxanim({
9939                     width : {to: this.adjustWidth(width * 2)},
9940                     height : {to: this.adjustHeight(height * 2)},
9941                     points : {by: [-(width * .5), -(height * .5)]},
9942                     opacity : {to: 0},
9943                     fontSize: {to:200, unit: "%"}
9944                 },
9945                 o,
9946                 'motion',
9947                 .5,
9948                 "easeOut", after);
9949         });
9950         return this;
9951     },
9952
9953         /**
9954          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9955          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9956          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9957          * Usage:
9958          *<pre><code>
9959 // default
9960 el.switchOff();
9961
9962 // all config options shown with default values
9963 el.switchOff({
9964     easing: 'easeIn',
9965     duration: .3,
9966     remove: false,
9967     useDisplay: false
9968 });
9969 </code></pre>
9970          * @param {Object} options (optional) Object literal with any of the Fx config options
9971          * @return {Roo.Element} The Element
9972          */
9973     switchOff : function(o){
9974         var el = this.getFxEl();
9975         o = o || {};
9976
9977         el.queueFx(o, function(){
9978             this.clearOpacity();
9979             this.clip();
9980
9981             // restore values after effect
9982             var r = this.getFxRestore();
9983             var st = this.dom.style;
9984
9985             var after = function(){
9986                 if(o.useDisplay){
9987                     el.setDisplayed(false);
9988                 }else{
9989                     el.hide();
9990                 }
9991
9992                 el.clearOpacity();
9993                 el.setPositioning(r.pos);
9994                 st.width = r.width;
9995                 st.height = r.height;
9996
9997                 el.afterFx(o);
9998             };
9999
10000             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10001                 this.clearOpacity();
10002                 (function(){
10003                     this.fxanim({
10004                         height:{to:1},
10005                         points:{by:[0, this.getHeight() * .5]}
10006                     }, o, 'motion', 0.3, 'easeIn', after);
10007                 }).defer(100, this);
10008             });
10009         });
10010         return this;
10011     },
10012
10013     /**
10014      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10015      * changed using the "attr" config option) and then fading back to the original color. If no original
10016      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10017      * Usage:
10018 <pre><code>
10019 // default: highlight background to yellow
10020 el.highlight();
10021
10022 // custom: highlight foreground text to blue for 2 seconds
10023 el.highlight("0000ff", { attr: 'color', duration: 2 });
10024
10025 // common config options shown with default values
10026 el.highlight("ffff9c", {
10027     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10028     endColor: (current color) or "ffffff",
10029     easing: 'easeIn',
10030     duration: 1
10031 });
10032 </code></pre>
10033      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10034      * @param {Object} options (optional) Object literal with any of the Fx config options
10035      * @return {Roo.Element} The Element
10036      */ 
10037     highlight : function(color, o){
10038         var el = this.getFxEl();
10039         o = o || {};
10040
10041         el.queueFx(o, function(){
10042             color = color || "ffff9c";
10043             attr = o.attr || "backgroundColor";
10044
10045             this.clearOpacity();
10046             this.show();
10047
10048             var origColor = this.getColor(attr);
10049             var restoreColor = this.dom.style[attr];
10050             endColor = (o.endColor || origColor) || "ffffff";
10051
10052             var after = function(){
10053                 el.dom.style[attr] = restoreColor;
10054                 el.afterFx(o);
10055             };
10056
10057             var a = {};
10058             a[attr] = {from: color, to: endColor};
10059             arguments.callee.anim = this.fxanim(a,
10060                 o,
10061                 'color',
10062                 1,
10063                 'easeIn', after);
10064         });
10065         return this;
10066     },
10067
10068    /**
10069     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10070     * Usage:
10071 <pre><code>
10072 // default: a single light blue ripple
10073 el.frame();
10074
10075 // custom: 3 red ripples lasting 3 seconds total
10076 el.frame("ff0000", 3, { duration: 3 });
10077
10078 // common config options shown with default values
10079 el.frame("C3DAF9", 1, {
10080     duration: 1 //duration of entire animation (not each individual ripple)
10081     // Note: Easing is not configurable and will be ignored if included
10082 });
10083 </code></pre>
10084     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10085     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10086     * @param {Object} options (optional) Object literal with any of the Fx config options
10087     * @return {Roo.Element} The Element
10088     */
10089     frame : function(color, count, o){
10090         var el = this.getFxEl();
10091         o = o || {};
10092
10093         el.queueFx(o, function(){
10094             color = color || "#C3DAF9";
10095             if(color.length == 6){
10096                 color = "#" + color;
10097             }
10098             count = count || 1;
10099             duration = o.duration || 1;
10100             this.show();
10101
10102             var b = this.getBox();
10103             var animFn = function(){
10104                 var proxy = this.createProxy({
10105
10106                      style:{
10107                         visbility:"hidden",
10108                         position:"absolute",
10109                         "z-index":"35000", // yee haw
10110                         border:"0px solid " + color
10111                      }
10112                   });
10113                 var scale = Roo.isBorderBox ? 2 : 1;
10114                 proxy.animate({
10115                     top:{from:b.y, to:b.y - 20},
10116                     left:{from:b.x, to:b.x - 20},
10117                     borderWidth:{from:0, to:10},
10118                     opacity:{from:1, to:0},
10119                     height:{from:b.height, to:(b.height + (20*scale))},
10120                     width:{from:b.width, to:(b.width + (20*scale))}
10121                 }, duration, function(){
10122                     proxy.remove();
10123                 });
10124                 if(--count > 0){
10125                      animFn.defer((duration/2)*1000, this);
10126                 }else{
10127                     el.afterFx(o);
10128                 }
10129             };
10130             animFn.call(this);
10131         });
10132         return this;
10133     },
10134
10135    /**
10136     * Creates a pause before any subsequent queued effects begin.  If there are
10137     * no effects queued after the pause it will have no effect.
10138     * Usage:
10139 <pre><code>
10140 el.pause(1);
10141 </code></pre>
10142     * @param {Number} seconds The length of time to pause (in seconds)
10143     * @return {Roo.Element} The Element
10144     */
10145     pause : function(seconds){
10146         var el = this.getFxEl();
10147         var o = {};
10148
10149         el.queueFx(o, function(){
10150             setTimeout(function(){
10151                 el.afterFx(o);
10152             }, seconds * 1000);
10153         });
10154         return this;
10155     },
10156
10157    /**
10158     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10159     * using the "endOpacity" config option.
10160     * Usage:
10161 <pre><code>
10162 // default: fade in from opacity 0 to 100%
10163 el.fadeIn();
10164
10165 // custom: fade in from opacity 0 to 75% over 2 seconds
10166 el.fadeIn({ endOpacity: .75, duration: 2});
10167
10168 // common config options shown with default values
10169 el.fadeIn({
10170     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10171     easing: 'easeOut',
10172     duration: .5
10173 });
10174 </code></pre>
10175     * @param {Object} options (optional) Object literal with any of the Fx config options
10176     * @return {Roo.Element} The Element
10177     */
10178     fadeIn : function(o){
10179         var el = this.getFxEl();
10180         o = o || {};
10181         el.queueFx(o, function(){
10182             this.setOpacity(0);
10183             this.fixDisplay();
10184             this.dom.style.visibility = 'visible';
10185             var to = o.endOpacity || 1;
10186             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10187                 o, null, .5, "easeOut", function(){
10188                 if(to == 1){
10189                     this.clearOpacity();
10190                 }
10191                 el.afterFx(o);
10192             });
10193         });
10194         return this;
10195     },
10196
10197    /**
10198     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10199     * using the "endOpacity" config option.
10200     * Usage:
10201 <pre><code>
10202 // default: fade out from the element's current opacity to 0
10203 el.fadeOut();
10204
10205 // custom: fade out from the element's current opacity to 25% over 2 seconds
10206 el.fadeOut({ endOpacity: .25, duration: 2});
10207
10208 // common config options shown with default values
10209 el.fadeOut({
10210     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10211     easing: 'easeOut',
10212     duration: .5
10213     remove: false,
10214     useDisplay: false
10215 });
10216 </code></pre>
10217     * @param {Object} options (optional) Object literal with any of the Fx config options
10218     * @return {Roo.Element} The Element
10219     */
10220     fadeOut : function(o){
10221         var el = this.getFxEl();
10222         o = o || {};
10223         el.queueFx(o, function(){
10224             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10225                 o, null, .5, "easeOut", function(){
10226                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10227                      this.dom.style.display = "none";
10228                 }else{
10229                      this.dom.style.visibility = "hidden";
10230                 }
10231                 this.clearOpacity();
10232                 el.afterFx(o);
10233             });
10234         });
10235         return this;
10236     },
10237
10238    /**
10239     * Animates the transition of an element's dimensions from a starting height/width
10240     * to an ending height/width.
10241     * Usage:
10242 <pre><code>
10243 // change height and width to 100x100 pixels
10244 el.scale(100, 100);
10245
10246 // common config options shown with default values.  The height and width will default to
10247 // the element's existing values if passed as null.
10248 el.scale(
10249     [element's width],
10250     [element's height], {
10251     easing: 'easeOut',
10252     duration: .35
10253 });
10254 </code></pre>
10255     * @param {Number} width  The new width (pass undefined to keep the original width)
10256     * @param {Number} height  The new height (pass undefined to keep the original height)
10257     * @param {Object} options (optional) Object literal with any of the Fx config options
10258     * @return {Roo.Element} The Element
10259     */
10260     scale : function(w, h, o){
10261         this.shift(Roo.apply({}, o, {
10262             width: w,
10263             height: h
10264         }));
10265         return this;
10266     },
10267
10268    /**
10269     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10270     * Any of these properties not specified in the config object will not be changed.  This effect 
10271     * requires that at least one new dimension, position or opacity setting must be passed in on
10272     * the config object in order for the function to have any effect.
10273     * Usage:
10274 <pre><code>
10275 // slide the element horizontally to x position 200 while changing the height and opacity
10276 el.shift({ x: 200, height: 50, opacity: .8 });
10277
10278 // common config options shown with default values.
10279 el.shift({
10280     width: [element's width],
10281     height: [element's height],
10282     x: [element's x position],
10283     y: [element's y position],
10284     opacity: [element's opacity],
10285     easing: 'easeOut',
10286     duration: .35
10287 });
10288 </code></pre>
10289     * @param {Object} options  Object literal with any of the Fx config options
10290     * @return {Roo.Element} The Element
10291     */
10292     shift : function(o){
10293         var el = this.getFxEl();
10294         o = o || {};
10295         el.queueFx(o, function(){
10296             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10297             if(w !== undefined){
10298                 a.width = {to: this.adjustWidth(w)};
10299             }
10300             if(h !== undefined){
10301                 a.height = {to: this.adjustHeight(h)};
10302             }
10303             if(x !== undefined || y !== undefined){
10304                 a.points = {to: [
10305                     x !== undefined ? x : this.getX(),
10306                     y !== undefined ? y : this.getY()
10307                 ]};
10308             }
10309             if(op !== undefined){
10310                 a.opacity = {to: op};
10311             }
10312             if(o.xy !== undefined){
10313                 a.points = {to: o.xy};
10314             }
10315             arguments.callee.anim = this.fxanim(a,
10316                 o, 'motion', .35, "easeOut", function(){
10317                 el.afterFx(o);
10318             });
10319         });
10320         return this;
10321     },
10322
10323         /**
10324          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10325          * ending point of the effect.
10326          * Usage:
10327          *<pre><code>
10328 // default: slide the element downward while fading out
10329 el.ghost();
10330
10331 // custom: slide the element out to the right with a 2-second duration
10332 el.ghost('r', { duration: 2 });
10333
10334 // common config options shown with default values
10335 el.ghost('b', {
10336     easing: 'easeOut',
10337     duration: .5
10338     remove: false,
10339     useDisplay: false
10340 });
10341 </code></pre>
10342          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10343          * @param {Object} options (optional) Object literal with any of the Fx config options
10344          * @return {Roo.Element} The Element
10345          */
10346     ghost : function(anchor, o){
10347         var el = this.getFxEl();
10348         o = o || {};
10349
10350         el.queueFx(o, function(){
10351             anchor = anchor || "b";
10352
10353             // restore values after effect
10354             var r = this.getFxRestore();
10355             var w = this.getWidth(),
10356                 h = this.getHeight();
10357
10358             var st = this.dom.style;
10359
10360             var after = function(){
10361                 if(o.useDisplay){
10362                     el.setDisplayed(false);
10363                 }else{
10364                     el.hide();
10365                 }
10366
10367                 el.clearOpacity();
10368                 el.setPositioning(r.pos);
10369                 st.width = r.width;
10370                 st.height = r.height;
10371
10372                 el.afterFx(o);
10373             };
10374
10375             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10376             switch(anchor.toLowerCase()){
10377                 case "t":
10378                     pt.by = [0, -h];
10379                 break;
10380                 case "l":
10381                     pt.by = [-w, 0];
10382                 break;
10383                 case "r":
10384                     pt.by = [w, 0];
10385                 break;
10386                 case "b":
10387                     pt.by = [0, h];
10388                 break;
10389                 case "tl":
10390                     pt.by = [-w, -h];
10391                 break;
10392                 case "bl":
10393                     pt.by = [-w, h];
10394                 break;
10395                 case "br":
10396                     pt.by = [w, h];
10397                 break;
10398                 case "tr":
10399                     pt.by = [w, -h];
10400                 break;
10401             }
10402
10403             arguments.callee.anim = this.fxanim(a,
10404                 o,
10405                 'motion',
10406                 .5,
10407                 "easeOut", after);
10408         });
10409         return this;
10410     },
10411
10412         /**
10413          * Ensures that all effects queued after syncFx is called on the element are
10414          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10415          * @return {Roo.Element} The Element
10416          */
10417     syncFx : function(){
10418         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10419             block : false,
10420             concurrent : true,
10421             stopFx : false
10422         });
10423         return this;
10424     },
10425
10426         /**
10427          * Ensures that all effects queued after sequenceFx is called on the element are
10428          * run in sequence.  This is the opposite of {@link #syncFx}.
10429          * @return {Roo.Element} The Element
10430          */
10431     sequenceFx : function(){
10432         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10433             block : false,
10434             concurrent : false,
10435             stopFx : false
10436         });
10437         return this;
10438     },
10439
10440         /* @private */
10441     nextFx : function(){
10442         var ef = this.fxQueue[0];
10443         if(ef){
10444             ef.call(this);
10445         }
10446     },
10447
10448         /**
10449          * Returns true if the element has any effects actively running or queued, else returns false.
10450          * @return {Boolean} True if element has active effects, else false
10451          */
10452     hasActiveFx : function(){
10453         return this.fxQueue && this.fxQueue[0];
10454     },
10455
10456         /**
10457          * Stops any running effects and clears the element's internal effects queue if it contains
10458          * any additional effects that haven't started yet.
10459          * @return {Roo.Element} The Element
10460          */
10461     stopFx : function(){
10462         if(this.hasActiveFx()){
10463             var cur = this.fxQueue[0];
10464             if(cur && cur.anim && cur.anim.isAnimated()){
10465                 this.fxQueue = [cur]; // clear out others
10466                 cur.anim.stop(true);
10467             }
10468         }
10469         return this;
10470     },
10471
10472         /* @private */
10473     beforeFx : function(o){
10474         if(this.hasActiveFx() && !o.concurrent){
10475            if(o.stopFx){
10476                this.stopFx();
10477                return true;
10478            }
10479            return false;
10480         }
10481         return true;
10482     },
10483
10484         /**
10485          * Returns true if the element is currently blocking so that no other effect can be queued
10486          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10487          * used to ensure that an effect initiated by a user action runs to completion prior to the
10488          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10489          * @return {Boolean} True if blocking, else false
10490          */
10491     hasFxBlock : function(){
10492         var q = this.fxQueue;
10493         return q && q[0] && q[0].block;
10494     },
10495
10496         /* @private */
10497     queueFx : function(o, fn){
10498         if(!this.fxQueue){
10499             this.fxQueue = [];
10500         }
10501         if(!this.hasFxBlock()){
10502             Roo.applyIf(o, this.fxDefaults);
10503             if(!o.concurrent){
10504                 var run = this.beforeFx(o);
10505                 fn.block = o.block;
10506                 this.fxQueue.push(fn);
10507                 if(run){
10508                     this.nextFx();
10509                 }
10510             }else{
10511                 fn.call(this);
10512             }
10513         }
10514         return this;
10515     },
10516
10517         /* @private */
10518     fxWrap : function(pos, o, vis){
10519         var wrap;
10520         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10521             var wrapXY;
10522             if(o.fixPosition){
10523                 wrapXY = this.getXY();
10524             }
10525             var div = document.createElement("div");
10526             div.style.visibility = vis;
10527             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10528             wrap.setPositioning(pos);
10529             if(wrap.getStyle("position") == "static"){
10530                 wrap.position("relative");
10531             }
10532             this.clearPositioning('auto');
10533             wrap.clip();
10534             wrap.dom.appendChild(this.dom);
10535             if(wrapXY){
10536                 wrap.setXY(wrapXY);
10537             }
10538         }
10539         return wrap;
10540     },
10541
10542         /* @private */
10543     fxUnwrap : function(wrap, pos, o){
10544         this.clearPositioning();
10545         this.setPositioning(pos);
10546         if(!o.wrap){
10547             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10548             wrap.remove();
10549         }
10550     },
10551
10552         /* @private */
10553     getFxRestore : function(){
10554         var st = this.dom.style;
10555         return {pos: this.getPositioning(), width: st.width, height : st.height};
10556     },
10557
10558         /* @private */
10559     afterFx : function(o){
10560         if(o.afterStyle){
10561             this.applyStyles(o.afterStyle);
10562         }
10563         if(o.afterCls){
10564             this.addClass(o.afterCls);
10565         }
10566         if(o.remove === true){
10567             this.remove();
10568         }
10569         Roo.callback(o.callback, o.scope, [this]);
10570         if(!o.concurrent){
10571             this.fxQueue.shift();
10572             this.nextFx();
10573         }
10574     },
10575
10576         /* @private */
10577     getFxEl : function(){ // support for composite element fx
10578         return Roo.get(this.dom);
10579     },
10580
10581         /* @private */
10582     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10583         animType = animType || 'run';
10584         opt = opt || {};
10585         var anim = Roo.lib.Anim[animType](
10586             this.dom, args,
10587             (opt.duration || defaultDur) || .35,
10588             (opt.easing || defaultEase) || 'easeOut',
10589             function(){
10590                 Roo.callback(cb, this);
10591             },
10592             this
10593         );
10594         opt.anim = anim;
10595         return anim;
10596     }
10597 };
10598
10599 // backwords compat
10600 Roo.Fx.resize = Roo.Fx.scale;
10601
10602 //When included, Roo.Fx is automatically applied to Element so that all basic
10603 //effects are available directly via the Element API
10604 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10605  * Based on:
10606  * Ext JS Library 1.1.1
10607  * Copyright(c) 2006-2007, Ext JS, LLC.
10608  *
10609  * Originally Released Under LGPL - original licence link has changed is not relivant.
10610  *
10611  * Fork - LGPL
10612  * <script type="text/javascript">
10613  */
10614
10615
10616 /**
10617  * @class Roo.CompositeElement
10618  * Standard composite class. Creates a Roo.Element for every element in the collection.
10619  * <br><br>
10620  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10621  * actions will be performed on all the elements in this collection.</b>
10622  * <br><br>
10623  * All methods return <i>this</i> and can be chained.
10624  <pre><code>
10625  var els = Roo.select("#some-el div.some-class", true);
10626  // or select directly from an existing element
10627  var el = Roo.get('some-el');
10628  el.select('div.some-class', true);
10629
10630  els.setWidth(100); // all elements become 100 width
10631  els.hide(true); // all elements fade out and hide
10632  // or
10633  els.setWidth(100).hide(true);
10634  </code></pre>
10635  */
10636 Roo.CompositeElement = function(els){
10637     this.elements = [];
10638     this.addElements(els);
10639 };
10640 Roo.CompositeElement.prototype = {
10641     isComposite: true,
10642     addElements : function(els){
10643         if(!els) return this;
10644         if(typeof els == "string"){
10645             els = Roo.Element.selectorFunction(els);
10646         }
10647         var yels = this.elements;
10648         var index = yels.length-1;
10649         for(var i = 0, len = els.length; i < len; i++) {
10650                 yels[++index] = Roo.get(els[i]);
10651         }
10652         return this;
10653     },
10654
10655     /**
10656     * Clears this composite and adds the elements returned by the passed selector.
10657     * @param {String/Array} els A string CSS selector, an array of elements or an element
10658     * @return {CompositeElement} this
10659     */
10660     fill : function(els){
10661         this.elements = [];
10662         this.add(els);
10663         return this;
10664     },
10665
10666     /**
10667     * Filters this composite to only elements that match the passed selector.
10668     * @param {String} selector A string CSS selector
10669     * @return {CompositeElement} this
10670     */
10671     filter : function(selector){
10672         var els = [];
10673         this.each(function(el){
10674             if(el.is(selector)){
10675                 els[els.length] = el.dom;
10676             }
10677         });
10678         this.fill(els);
10679         return this;
10680     },
10681
10682     invoke : function(fn, args){
10683         var els = this.elements;
10684         for(var i = 0, len = els.length; i < len; i++) {
10685                 Roo.Element.prototype[fn].apply(els[i], args);
10686         }
10687         return this;
10688     },
10689     /**
10690     * Adds elements to this composite.
10691     * @param {String/Array} els A string CSS selector, an array of elements or an element
10692     * @return {CompositeElement} this
10693     */
10694     add : function(els){
10695         if(typeof els == "string"){
10696             this.addElements(Roo.Element.selectorFunction(els));
10697         }else if(els.length !== undefined){
10698             this.addElements(els);
10699         }else{
10700             this.addElements([els]);
10701         }
10702         return this;
10703     },
10704     /**
10705     * Calls the passed function passing (el, this, index) for each element in this composite.
10706     * @param {Function} fn The function to call
10707     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10708     * @return {CompositeElement} this
10709     */
10710     each : function(fn, scope){
10711         var els = this.elements;
10712         for(var i = 0, len = els.length; i < len; i++){
10713             if(fn.call(scope || els[i], els[i], this, i) === false) {
10714                 break;
10715             }
10716         }
10717         return this;
10718     },
10719
10720     /**
10721      * Returns the Element object at the specified index
10722      * @param {Number} index
10723      * @return {Roo.Element}
10724      */
10725     item : function(index){
10726         return this.elements[index] || null;
10727     },
10728
10729     /**
10730      * Returns the first Element
10731      * @return {Roo.Element}
10732      */
10733     first : function(){
10734         return this.item(0);
10735     },
10736
10737     /**
10738      * Returns the last Element
10739      * @return {Roo.Element}
10740      */
10741     last : function(){
10742         return this.item(this.elements.length-1);
10743     },
10744
10745     /**
10746      * Returns the number of elements in this composite
10747      * @return Number
10748      */
10749     getCount : function(){
10750         return this.elements.length;
10751     },
10752
10753     /**
10754      * Returns true if this composite contains the passed element
10755      * @return Boolean
10756      */
10757     contains : function(el){
10758         return this.indexOf(el) !== -1;
10759     },
10760
10761     /**
10762      * Returns true if this composite contains the passed element
10763      * @return Boolean
10764      */
10765     indexOf : function(el){
10766         return this.elements.indexOf(Roo.get(el));
10767     },
10768
10769
10770     /**
10771     * Removes the specified element(s).
10772     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10773     * or an array of any of those.
10774     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10775     * @return {CompositeElement} this
10776     */
10777     removeElement : function(el, removeDom){
10778         if(el instanceof Array){
10779             for(var i = 0, len = el.length; i < len; i++){
10780                 this.removeElement(el[i]);
10781             }
10782             return this;
10783         }
10784         var index = typeof el == 'number' ? el : this.indexOf(el);
10785         if(index !== -1){
10786             if(removeDom){
10787                 var d = this.elements[index];
10788                 if(d.dom){
10789                     d.remove();
10790                 }else{
10791                     d.parentNode.removeChild(d);
10792                 }
10793             }
10794             this.elements.splice(index, 1);
10795         }
10796         return this;
10797     },
10798
10799     /**
10800     * Replaces the specified element with the passed element.
10801     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10802     * to replace.
10803     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10804     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10805     * @return {CompositeElement} this
10806     */
10807     replaceElement : function(el, replacement, domReplace){
10808         var index = typeof el == 'number' ? el : this.indexOf(el);
10809         if(index !== -1){
10810             if(domReplace){
10811                 this.elements[index].replaceWith(replacement);
10812             }else{
10813                 this.elements.splice(index, 1, Roo.get(replacement))
10814             }
10815         }
10816         return this;
10817     },
10818
10819     /**
10820      * Removes all elements.
10821      */
10822     clear : function(){
10823         this.elements = [];
10824     }
10825 };
10826 (function(){
10827     Roo.CompositeElement.createCall = function(proto, fnName){
10828         if(!proto[fnName]){
10829             proto[fnName] = function(){
10830                 return this.invoke(fnName, arguments);
10831             };
10832         }
10833     };
10834     for(var fnName in Roo.Element.prototype){
10835         if(typeof Roo.Element.prototype[fnName] == "function"){
10836             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10837         }
10838     };
10839 })();
10840 /*
10841  * Based on:
10842  * Ext JS Library 1.1.1
10843  * Copyright(c) 2006-2007, Ext JS, LLC.
10844  *
10845  * Originally Released Under LGPL - original licence link has changed is not relivant.
10846  *
10847  * Fork - LGPL
10848  * <script type="text/javascript">
10849  */
10850
10851 /**
10852  * @class Roo.CompositeElementLite
10853  * @extends Roo.CompositeElement
10854  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10855  <pre><code>
10856  var els = Roo.select("#some-el div.some-class");
10857  // or select directly from an existing element
10858  var el = Roo.get('some-el');
10859  el.select('div.some-class');
10860
10861  els.setWidth(100); // all elements become 100 width
10862  els.hide(true); // all elements fade out and hide
10863  // or
10864  els.setWidth(100).hide(true);
10865  </code></pre><br><br>
10866  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10867  * actions will be performed on all the elements in this collection.</b>
10868  */
10869 Roo.CompositeElementLite = function(els){
10870     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10871     this.el = new Roo.Element.Flyweight();
10872 };
10873 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10874     addElements : function(els){
10875         if(els){
10876             if(els instanceof Array){
10877                 this.elements = this.elements.concat(els);
10878             }else{
10879                 var yels = this.elements;
10880                 var index = yels.length-1;
10881                 for(var i = 0, len = els.length; i < len; i++) {
10882                     yels[++index] = els[i];
10883                 }
10884             }
10885         }
10886         return this;
10887     },
10888     invoke : function(fn, args){
10889         var els = this.elements;
10890         var el = this.el;
10891         for(var i = 0, len = els.length; i < len; i++) {
10892             el.dom = els[i];
10893                 Roo.Element.prototype[fn].apply(el, args);
10894         }
10895         return this;
10896     },
10897     /**
10898      * Returns a flyweight Element of the dom element object at the specified index
10899      * @param {Number} index
10900      * @return {Roo.Element}
10901      */
10902     item : function(index){
10903         if(!this.elements[index]){
10904             return null;
10905         }
10906         this.el.dom = this.elements[index];
10907         return this.el;
10908     },
10909
10910     // fixes scope with flyweight
10911     addListener : function(eventName, handler, scope, opt){
10912         var els = this.elements;
10913         for(var i = 0, len = els.length; i < len; i++) {
10914             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10915         }
10916         return this;
10917     },
10918
10919     /**
10920     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10921     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10922     * a reference to the dom node, use el.dom.</b>
10923     * @param {Function} fn The function to call
10924     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10925     * @return {CompositeElement} this
10926     */
10927     each : function(fn, scope){
10928         var els = this.elements;
10929         var el = this.el;
10930         for(var i = 0, len = els.length; i < len; i++){
10931             el.dom = els[i];
10932                 if(fn.call(scope || el, el, this, i) === false){
10933                 break;
10934             }
10935         }
10936         return this;
10937     },
10938
10939     indexOf : function(el){
10940         return this.elements.indexOf(Roo.getDom(el));
10941     },
10942
10943     replaceElement : function(el, replacement, domReplace){
10944         var index = typeof el == 'number' ? el : this.indexOf(el);
10945         if(index !== -1){
10946             replacement = Roo.getDom(replacement);
10947             if(domReplace){
10948                 var d = this.elements[index];
10949                 d.parentNode.insertBefore(replacement, d);
10950                 d.parentNode.removeChild(d);
10951             }
10952             this.elements.splice(index, 1, replacement);
10953         }
10954         return this;
10955     }
10956 });
10957 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10958
10959 /*
10960  * Based on:
10961  * Ext JS Library 1.1.1
10962  * Copyright(c) 2006-2007, Ext JS, LLC.
10963  *
10964  * Originally Released Under LGPL - original licence link has changed is not relivant.
10965  *
10966  * Fork - LGPL
10967  * <script type="text/javascript">
10968  */
10969
10970  
10971
10972 /**
10973  * @class Roo.data.Connection
10974  * @extends Roo.util.Observable
10975  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10976  * either to a configured URL, or to a URL specified at request time.<br><br>
10977  * <p>
10978  * Requests made by this class are asynchronous, and will return immediately. No data from
10979  * the server will be available to the statement immediately following the {@link #request} call.
10980  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10981  * <p>
10982  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10983  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10984  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10985  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10986  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10987  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10988  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10989  * standard DOM methods.
10990  * @constructor
10991  * @param {Object} config a configuration object.
10992  */
10993 Roo.data.Connection = function(config){
10994     Roo.apply(this, config);
10995     this.addEvents({
10996         /**
10997          * @event beforerequest
10998          * Fires before a network request is made to retrieve a data object.
10999          * @param {Connection} conn This Connection object.
11000          * @param {Object} options The options config object passed to the {@link #request} method.
11001          */
11002         "beforerequest" : true,
11003         /**
11004          * @event requestcomplete
11005          * Fires if the request was successfully completed.
11006          * @param {Connection} conn This Connection object.
11007          * @param {Object} response The XHR object containing the response data.
11008          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11009          * @param {Object} options The options config object passed to the {@link #request} method.
11010          */
11011         "requestcomplete" : true,
11012         /**
11013          * @event requestexception
11014          * Fires if an error HTTP status was returned from the server.
11015          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11016          * @param {Connection} conn This Connection object.
11017          * @param {Object} response The XHR object containing the response data.
11018          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11019          * @param {Object} options The options config object passed to the {@link #request} method.
11020          */
11021         "requestexception" : true
11022     });
11023     Roo.data.Connection.superclass.constructor.call(this);
11024 };
11025
11026 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11027     /**
11028      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11029      */
11030     /**
11031      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11032      * extra parameters to each request made by this object. (defaults to undefined)
11033      */
11034     /**
11035      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11036      *  to each request made by this object. (defaults to undefined)
11037      */
11038     /**
11039      * @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)
11040      */
11041     /**
11042      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11043      */
11044     timeout : 30000,
11045     /**
11046      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11047      * @type Boolean
11048      */
11049     autoAbort:false,
11050
11051     /**
11052      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11053      * @type Boolean
11054      */
11055     disableCaching: true,
11056
11057     /**
11058      * Sends an HTTP request to a remote server.
11059      * @param {Object} options An object which may contain the following properties:<ul>
11060      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11061      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11062      * request, a url encoded string or a function to call to get either.</li>
11063      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11064      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11065      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11066      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11067      * <li>options {Object} The parameter to the request call.</li>
11068      * <li>success {Boolean} True if the request succeeded.</li>
11069      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11070      * </ul></li>
11071      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11072      * The callback is passed the following parameters:<ul>
11073      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11074      * <li>options {Object} The parameter to the request call.</li>
11075      * </ul></li>
11076      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11077      * The callback is passed the following parameters:<ul>
11078      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11079      * <li>options {Object} The parameter to the request call.</li>
11080      * </ul></li>
11081      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11082      * for the callback function. Defaults to the browser window.</li>
11083      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11084      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11085      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11086      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11087      * params for the post data. Any params will be appended to the URL.</li>
11088      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11089      * </ul>
11090      * @return {Number} transactionId
11091      */
11092     request : function(o){
11093         if(this.fireEvent("beforerequest", this, o) !== false){
11094             var p = o.params;
11095
11096             if(typeof p == "function"){
11097                 p = p.call(o.scope||window, o);
11098             }
11099             if(typeof p == "object"){
11100                 p = Roo.urlEncode(o.params);
11101             }
11102             if(this.extraParams){
11103                 var extras = Roo.urlEncode(this.extraParams);
11104                 p = p ? (p + '&' + extras) : extras;
11105             }
11106
11107             var url = o.url || this.url;
11108             if(typeof url == 'function'){
11109                 url = url.call(o.scope||window, o);
11110             }
11111
11112             if(o.form){
11113                 var form = Roo.getDom(o.form);
11114                 url = url || form.action;
11115
11116                 var enctype = form.getAttribute("enctype");
11117                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11118                     return this.doFormUpload(o, p, url);
11119                 }
11120                 var f = Roo.lib.Ajax.serializeForm(form);
11121                 p = p ? (p + '&' + f) : f;
11122             }
11123
11124             var hs = o.headers;
11125             if(this.defaultHeaders){
11126                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11127                 if(!o.headers){
11128                     o.headers = hs;
11129                 }
11130             }
11131
11132             var cb = {
11133                 success: this.handleResponse,
11134                 failure: this.handleFailure,
11135                 scope: this,
11136                 argument: {options: o},
11137                 timeout : this.timeout
11138             };
11139
11140             var method = o.method||this.method||(p ? "POST" : "GET");
11141
11142             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11143                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11144             }
11145
11146             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11147                 if(o.autoAbort){
11148                     this.abort();
11149                 }
11150             }else if(this.autoAbort !== false){
11151                 this.abort();
11152             }
11153
11154             if((method == 'GET' && p) || o.xmlData){
11155                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11156                 p = '';
11157             }
11158             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11159             return this.transId;
11160         }else{
11161             Roo.callback(o.callback, o.scope, [o, null, null]);
11162             return null;
11163         }
11164     },
11165
11166     /**
11167      * Determine whether this object has a request outstanding.
11168      * @param {Number} transactionId (Optional) defaults to the last transaction
11169      * @return {Boolean} True if there is an outstanding request.
11170      */
11171     isLoading : function(transId){
11172         if(transId){
11173             return Roo.lib.Ajax.isCallInProgress(transId);
11174         }else{
11175             return this.transId ? true : false;
11176         }
11177     },
11178
11179     /**
11180      * Aborts any outstanding request.
11181      * @param {Number} transactionId (Optional) defaults to the last transaction
11182      */
11183     abort : function(transId){
11184         if(transId || this.isLoading()){
11185             Roo.lib.Ajax.abort(transId || this.transId);
11186         }
11187     },
11188
11189     // private
11190     handleResponse : function(response){
11191         this.transId = false;
11192         var options = response.argument.options;
11193         response.argument = options ? options.argument : null;
11194         this.fireEvent("requestcomplete", this, response, options);
11195         Roo.callback(options.success, options.scope, [response, options]);
11196         Roo.callback(options.callback, options.scope, [options, true, response]);
11197     },
11198
11199     // private
11200     handleFailure : function(response, e){
11201         this.transId = false;
11202         var options = response.argument.options;
11203         response.argument = options ? options.argument : null;
11204         this.fireEvent("requestexception", this, response, options, e);
11205         Roo.callback(options.failure, options.scope, [response, options]);
11206         Roo.callback(options.callback, options.scope, [options, false, response]);
11207     },
11208
11209     // private
11210     doFormUpload : function(o, ps, url){
11211         var id = Roo.id();
11212         var frame = document.createElement('iframe');
11213         frame.id = id;
11214         frame.name = id;
11215         frame.className = 'x-hidden';
11216         if(Roo.isIE){
11217             frame.src = Roo.SSL_SECURE_URL;
11218         }
11219         document.body.appendChild(frame);
11220
11221         if(Roo.isIE){
11222            document.frames[id].name = id;
11223         }
11224
11225         var form = Roo.getDom(o.form);
11226         form.target = id;
11227         form.method = 'POST';
11228         form.enctype = form.encoding = 'multipart/form-data';
11229         if(url){
11230             form.action = url;
11231         }
11232
11233         var hiddens, hd;
11234         if(ps){ // add dynamic params
11235             hiddens = [];
11236             ps = Roo.urlDecode(ps, false);
11237             for(var k in ps){
11238                 if(ps.hasOwnProperty(k)){
11239                     hd = document.createElement('input');
11240                     hd.type = 'hidden';
11241                     hd.name = k;
11242                     hd.value = ps[k];
11243                     form.appendChild(hd);
11244                     hiddens.push(hd);
11245                 }
11246             }
11247         }
11248
11249         function cb(){
11250             var r = {  // bogus response object
11251                 responseText : '',
11252                 responseXML : null
11253             };
11254
11255             r.argument = o ? o.argument : null;
11256
11257             try { //
11258                 var doc;
11259                 if(Roo.isIE){
11260                     doc = frame.contentWindow.document;
11261                 }else {
11262                     doc = (frame.contentDocument || window.frames[id].document);
11263                 }
11264                 if(doc && doc.body){
11265                     r.responseText = doc.body.innerHTML;
11266                 }
11267                 if(doc && doc.XMLDocument){
11268                     r.responseXML = doc.XMLDocument;
11269                 }else {
11270                     r.responseXML = doc;
11271                 }
11272             }
11273             catch(e) {
11274                 // ignore
11275             }
11276
11277             Roo.EventManager.removeListener(frame, 'load', cb, this);
11278
11279             this.fireEvent("requestcomplete", this, r, o);
11280             Roo.callback(o.success, o.scope, [r, o]);
11281             Roo.callback(o.callback, o.scope, [o, true, r]);
11282
11283             setTimeout(function(){document.body.removeChild(frame);}, 100);
11284         }
11285
11286         Roo.EventManager.on(frame, 'load', cb, this);
11287         form.submit();
11288
11289         if(hiddens){ // remove dynamic params
11290             for(var i = 0, len = hiddens.length; i < len; i++){
11291                 form.removeChild(hiddens[i]);
11292             }
11293         }
11294     }
11295 });
11296
11297 /**
11298  * @class Roo.Ajax
11299  * @extends Roo.data.Connection
11300  * Global Ajax request class.
11301  *
11302  * @singleton
11303  */
11304 Roo.Ajax = new Roo.data.Connection({
11305     // fix up the docs
11306    /**
11307      * @cfg {String} url @hide
11308      */
11309     /**
11310      * @cfg {Object} extraParams @hide
11311      */
11312     /**
11313      * @cfg {Object} defaultHeaders @hide
11314      */
11315     /**
11316      * @cfg {String} method (Optional) @hide
11317      */
11318     /**
11319      * @cfg {Number} timeout (Optional) @hide
11320      */
11321     /**
11322      * @cfg {Boolean} autoAbort (Optional) @hide
11323      */
11324
11325     /**
11326      * @cfg {Boolean} disableCaching (Optional) @hide
11327      */
11328
11329     /**
11330      * @property  disableCaching
11331      * True to add a unique cache-buster param to GET requests. (defaults to true)
11332      * @type Boolean
11333      */
11334     /**
11335      * @property  url
11336      * The default URL to be used for requests to the server. (defaults to undefined)
11337      * @type String
11338      */
11339     /**
11340      * @property  extraParams
11341      * An object containing properties which are used as
11342      * extra parameters to each request made by this object. (defaults to undefined)
11343      * @type Object
11344      */
11345     /**
11346      * @property  defaultHeaders
11347      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11348      * @type Object
11349      */
11350     /**
11351      * @property  method
11352      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11353      * @type String
11354      */
11355     /**
11356      * @property  timeout
11357      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11358      * @type Number
11359      */
11360
11361     /**
11362      * @property  autoAbort
11363      * Whether a new request should abort any pending requests. (defaults to false)
11364      * @type Boolean
11365      */
11366     autoAbort : false,
11367
11368     /**
11369      * Serialize the passed form into a url encoded string
11370      * @param {String/HTMLElement} form
11371      * @return {String}
11372      */
11373     serializeForm : function(form){
11374         return Roo.lib.Ajax.serializeForm(form);
11375     }
11376 });/*
11377  * Based on:
11378  * Ext JS Library 1.1.1
11379  * Copyright(c) 2006-2007, Ext JS, LLC.
11380  *
11381  * Originally Released Under LGPL - original licence link has changed is not relivant.
11382  *
11383  * Fork - LGPL
11384  * <script type="text/javascript">
11385  */
11386  
11387 /**
11388  * @class Roo.Ajax
11389  * @extends Roo.data.Connection
11390  * Global Ajax request class.
11391  *
11392  * @instanceOf  Roo.data.Connection
11393  */
11394 Roo.Ajax = new Roo.data.Connection({
11395     // fix up the docs
11396     
11397     /**
11398      * fix up scoping
11399      * @scope Roo.Ajax
11400      */
11401     
11402    /**
11403      * @cfg {String} url @hide
11404      */
11405     /**
11406      * @cfg {Object} extraParams @hide
11407      */
11408     /**
11409      * @cfg {Object} defaultHeaders @hide
11410      */
11411     /**
11412      * @cfg {String} method (Optional) @hide
11413      */
11414     /**
11415      * @cfg {Number} timeout (Optional) @hide
11416      */
11417     /**
11418      * @cfg {Boolean} autoAbort (Optional) @hide
11419      */
11420
11421     /**
11422      * @cfg {Boolean} disableCaching (Optional) @hide
11423      */
11424
11425     /**
11426      * @property  disableCaching
11427      * True to add a unique cache-buster param to GET requests. (defaults to true)
11428      * @type Boolean
11429      */
11430     /**
11431      * @property  url
11432      * The default URL to be used for requests to the server. (defaults to undefined)
11433      * @type String
11434      */
11435     /**
11436      * @property  extraParams
11437      * An object containing properties which are used as
11438      * extra parameters to each request made by this object. (defaults to undefined)
11439      * @type Object
11440      */
11441     /**
11442      * @property  defaultHeaders
11443      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11444      * @type Object
11445      */
11446     /**
11447      * @property  method
11448      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11449      * @type String
11450      */
11451     /**
11452      * @property  timeout
11453      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11454      * @type Number
11455      */
11456
11457     /**
11458      * @property  autoAbort
11459      * Whether a new request should abort any pending requests. (defaults to false)
11460      * @type Boolean
11461      */
11462     autoAbort : false,
11463
11464     /**
11465      * Serialize the passed form into a url encoded string
11466      * @param {String/HTMLElement} form
11467      * @return {String}
11468      */
11469     serializeForm : function(form){
11470         return Roo.lib.Ajax.serializeForm(form);
11471     }
11472 });/*
11473  * Based on:
11474  * Ext JS Library 1.1.1
11475  * Copyright(c) 2006-2007, Ext JS, LLC.
11476  *
11477  * Originally Released Under LGPL - original licence link has changed is not relivant.
11478  *
11479  * Fork - LGPL
11480  * <script type="text/javascript">
11481  */
11482
11483  
11484 /**
11485  * @class Roo.UpdateManager
11486  * @extends Roo.util.Observable
11487  * Provides AJAX-style update for Element object.<br><br>
11488  * Usage:<br>
11489  * <pre><code>
11490  * // Get it from a Roo.Element object
11491  * var el = Roo.get("foo");
11492  * var mgr = el.getUpdateManager();
11493  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11494  * ...
11495  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11496  * <br>
11497  * // or directly (returns the same UpdateManager instance)
11498  * var mgr = new Roo.UpdateManager("myElementId");
11499  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11500  * mgr.on("update", myFcnNeedsToKnow);
11501  * <br>
11502    // short handed call directly from the element object
11503    Roo.get("foo").load({
11504         url: "bar.php",
11505         scripts:true,
11506         params: "for=bar",
11507         text: "Loading Foo..."
11508    });
11509  * </code></pre>
11510  * @constructor
11511  * Create new UpdateManager directly.
11512  * @param {String/HTMLElement/Roo.Element} el The element to update
11513  * @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).
11514  */
11515 Roo.UpdateManager = function(el, forceNew){
11516     el = Roo.get(el);
11517     if(!forceNew && el.updateManager){
11518         return el.updateManager;
11519     }
11520     /**
11521      * The Element object
11522      * @type Roo.Element
11523      */
11524     this.el = el;
11525     /**
11526      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11527      * @type String
11528      */
11529     this.defaultUrl = null;
11530
11531     this.addEvents({
11532         /**
11533          * @event beforeupdate
11534          * Fired before an update is made, return false from your handler and the update is cancelled.
11535          * @param {Roo.Element} el
11536          * @param {String/Object/Function} url
11537          * @param {String/Object} params
11538          */
11539         "beforeupdate": true,
11540         /**
11541          * @event update
11542          * Fired after successful update is made.
11543          * @param {Roo.Element} el
11544          * @param {Object} oResponseObject The response Object
11545          */
11546         "update": true,
11547         /**
11548          * @event failure
11549          * Fired on update failure.
11550          * @param {Roo.Element} el
11551          * @param {Object} oResponseObject The response Object
11552          */
11553         "failure": true
11554     });
11555     var d = Roo.UpdateManager.defaults;
11556     /**
11557      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11558      * @type String
11559      */
11560     this.sslBlankUrl = d.sslBlankUrl;
11561     /**
11562      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11563      * @type Boolean
11564      */
11565     this.disableCaching = d.disableCaching;
11566     /**
11567      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11568      * @type String
11569      */
11570     this.indicatorText = d.indicatorText;
11571     /**
11572      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11573      * @type String
11574      */
11575     this.showLoadIndicator = d.showLoadIndicator;
11576     /**
11577      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11578      * @type Number
11579      */
11580     this.timeout = d.timeout;
11581
11582     /**
11583      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11584      * @type Boolean
11585      */
11586     this.loadScripts = d.loadScripts;
11587
11588     /**
11589      * Transaction object of current executing transaction
11590      */
11591     this.transaction = null;
11592
11593     /**
11594      * @private
11595      */
11596     this.autoRefreshProcId = null;
11597     /**
11598      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11599      * @type Function
11600      */
11601     this.refreshDelegate = this.refresh.createDelegate(this);
11602     /**
11603      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11604      * @type Function
11605      */
11606     this.updateDelegate = this.update.createDelegate(this);
11607     /**
11608      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11609      * @type Function
11610      */
11611     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11612     /**
11613      * @private
11614      */
11615     this.successDelegate = this.processSuccess.createDelegate(this);
11616     /**
11617      * @private
11618      */
11619     this.failureDelegate = this.processFailure.createDelegate(this);
11620
11621     if(!this.renderer){
11622      /**
11623       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11624       */
11625     this.renderer = new Roo.UpdateManager.BasicRenderer();
11626     }
11627     
11628     Roo.UpdateManager.superclass.constructor.call(this);
11629 };
11630
11631 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11632     /**
11633      * Get the Element this UpdateManager is bound to
11634      * @return {Roo.Element} The element
11635      */
11636     getEl : function(){
11637         return this.el;
11638     },
11639     /**
11640      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11641      * @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:
11642 <pre><code>
11643 um.update({<br/>
11644     url: "your-url.php",<br/>
11645     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11646     callback: yourFunction,<br/>
11647     scope: yourObject, //(optional scope)  <br/>
11648     discardUrl: false, <br/>
11649     nocache: false,<br/>
11650     text: "Loading...",<br/>
11651     timeout: 30,<br/>
11652     scripts: false<br/>
11653 });
11654 </code></pre>
11655      * The only required property is url. The optional properties nocache, text and scripts
11656      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11657      * @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}
11658      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11659      * @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.
11660      */
11661     update : function(url, params, callback, discardUrl){
11662         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11663             var method = this.method, cfg;
11664             if(typeof url == "object"){ // must be config object
11665                 cfg = url;
11666                 url = cfg.url;
11667                 params = params || cfg.params;
11668                 callback = callback || cfg.callback;
11669                 discardUrl = discardUrl || cfg.discardUrl;
11670                 if(callback && cfg.scope){
11671                     callback = callback.createDelegate(cfg.scope);
11672                 }
11673                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11674                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11675                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11676                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11677                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11678             }
11679             this.showLoading();
11680             if(!discardUrl){
11681                 this.defaultUrl = url;
11682             }
11683             if(typeof url == "function"){
11684                 url = url.call(this);
11685             }
11686
11687             method = method || (params ? "POST" : "GET");
11688             if(method == "GET"){
11689                 url = this.prepareUrl(url);
11690             }
11691
11692             var o = Roo.apply(cfg ||{}, {
11693                 url : url,
11694                 params: params,
11695                 success: this.successDelegate,
11696                 failure: this.failureDelegate,
11697                 callback: undefined,
11698                 timeout: (this.timeout*1000),
11699                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11700             });
11701
11702             this.transaction = Roo.Ajax.request(o);
11703         }
11704     },
11705
11706     /**
11707      * 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.
11708      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11709      * @param {String/HTMLElement} form The form Id or form element
11710      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11711      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11712      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11713      */
11714     formUpdate : function(form, url, reset, callback){
11715         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11716             if(typeof url == "function"){
11717                 url = url.call(this);
11718             }
11719             form = Roo.getDom(form);
11720             this.transaction = Roo.Ajax.request({
11721                 form: form,
11722                 url:url,
11723                 success: this.successDelegate,
11724                 failure: this.failureDelegate,
11725                 timeout: (this.timeout*1000),
11726                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11727             });
11728             this.showLoading.defer(1, this);
11729         }
11730     },
11731
11732     /**
11733      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11734      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11735      */
11736     refresh : function(callback){
11737         if(this.defaultUrl == null){
11738             return;
11739         }
11740         this.update(this.defaultUrl, null, callback, true);
11741     },
11742
11743     /**
11744      * Set this element to auto refresh.
11745      * @param {Number} interval How often to update (in seconds).
11746      * @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)
11747      * @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}
11748      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11749      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11750      */
11751     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11752         if(refreshNow){
11753             this.update(url || this.defaultUrl, params, callback, true);
11754         }
11755         if(this.autoRefreshProcId){
11756             clearInterval(this.autoRefreshProcId);
11757         }
11758         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11759     },
11760
11761     /**
11762      * Stop auto refresh on this element.
11763      */
11764      stopAutoRefresh : function(){
11765         if(this.autoRefreshProcId){
11766             clearInterval(this.autoRefreshProcId);
11767             delete this.autoRefreshProcId;
11768         }
11769     },
11770
11771     isAutoRefreshing : function(){
11772        return this.autoRefreshProcId ? true : false;
11773     },
11774     /**
11775      * Called to update the element to "Loading" state. Override to perform custom action.
11776      */
11777     showLoading : function(){
11778         if(this.showLoadIndicator){
11779             this.el.update(this.indicatorText);
11780         }
11781     },
11782
11783     /**
11784      * Adds unique parameter to query string if disableCaching = true
11785      * @private
11786      */
11787     prepareUrl : function(url){
11788         if(this.disableCaching){
11789             var append = "_dc=" + (new Date().getTime());
11790             if(url.indexOf("?") !== -1){
11791                 url += "&" + append;
11792             }else{
11793                 url += "?" + append;
11794             }
11795         }
11796         return url;
11797     },
11798
11799     /**
11800      * @private
11801      */
11802     processSuccess : function(response){
11803         this.transaction = null;
11804         if(response.argument.form && response.argument.reset){
11805             try{ // put in try/catch since some older FF releases had problems with this
11806                 response.argument.form.reset();
11807             }catch(e){}
11808         }
11809         if(this.loadScripts){
11810             this.renderer.render(this.el, response, this,
11811                 this.updateComplete.createDelegate(this, [response]));
11812         }else{
11813             this.renderer.render(this.el, response, this);
11814             this.updateComplete(response);
11815         }
11816     },
11817
11818     updateComplete : function(response){
11819         this.fireEvent("update", this.el, response);
11820         if(typeof response.argument.callback == "function"){
11821             response.argument.callback(this.el, true, response);
11822         }
11823     },
11824
11825     /**
11826      * @private
11827      */
11828     processFailure : function(response){
11829         this.transaction = null;
11830         this.fireEvent("failure", this.el, response);
11831         if(typeof response.argument.callback == "function"){
11832             response.argument.callback(this.el, false, response);
11833         }
11834     },
11835
11836     /**
11837      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11838      * @param {Object} renderer The object implementing the render() method
11839      */
11840     setRenderer : function(renderer){
11841         this.renderer = renderer;
11842     },
11843
11844     getRenderer : function(){
11845        return this.renderer;
11846     },
11847
11848     /**
11849      * Set the defaultUrl used for updates
11850      * @param {String/Function} defaultUrl The url or a function to call to get the url
11851      */
11852     setDefaultUrl : function(defaultUrl){
11853         this.defaultUrl = defaultUrl;
11854     },
11855
11856     /**
11857      * Aborts the executing transaction
11858      */
11859     abort : function(){
11860         if(this.transaction){
11861             Roo.Ajax.abort(this.transaction);
11862         }
11863     },
11864
11865     /**
11866      * Returns true if an update is in progress
11867      * @return {Boolean}
11868      */
11869     isUpdating : function(){
11870         if(this.transaction){
11871             return Roo.Ajax.isLoading(this.transaction);
11872         }
11873         return false;
11874     }
11875 });
11876
11877 /**
11878  * @class Roo.UpdateManager.defaults
11879  * @static (not really - but it helps the doc tool)
11880  * The defaults collection enables customizing the default properties of UpdateManager
11881  */
11882    Roo.UpdateManager.defaults = {
11883        /**
11884          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11885          * @type Number
11886          */
11887          timeout : 30,
11888
11889          /**
11890          * True to process scripts by default (Defaults to false).
11891          * @type Boolean
11892          */
11893         loadScripts : false,
11894
11895         /**
11896         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11897         * @type String
11898         */
11899         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11900         /**
11901          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11902          * @type Boolean
11903          */
11904         disableCaching : false,
11905         /**
11906          * Whether to show indicatorText when loading (Defaults to true).
11907          * @type Boolean
11908          */
11909         showLoadIndicator : true,
11910         /**
11911          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11912          * @type String
11913          */
11914         indicatorText : '<div class="loading-indicator">Loading...</div>'
11915    };
11916
11917 /**
11918  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11919  *Usage:
11920  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11921  * @param {String/HTMLElement/Roo.Element} el The element to update
11922  * @param {String} url The url
11923  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11924  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11925  * @static
11926  * @deprecated
11927  * @member Roo.UpdateManager
11928  */
11929 Roo.UpdateManager.updateElement = function(el, url, params, options){
11930     var um = Roo.get(el, true).getUpdateManager();
11931     Roo.apply(um, options);
11932     um.update(url, params, options ? options.callback : null);
11933 };
11934 // alias for backwards compat
11935 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11936 /**
11937  * @class Roo.UpdateManager.BasicRenderer
11938  * Default Content renderer. Updates the elements innerHTML with the responseText.
11939  */
11940 Roo.UpdateManager.BasicRenderer = function(){};
11941
11942 Roo.UpdateManager.BasicRenderer.prototype = {
11943     /**
11944      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11945      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11946      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11947      * @param {Roo.Element} el The element being rendered
11948      * @param {Object} response The YUI Connect response object
11949      * @param {UpdateManager} updateManager The calling update manager
11950      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11951      */
11952      render : function(el, response, updateManager, callback){
11953         el.update(response.responseText, updateManager.loadScripts, callback);
11954     }
11955 };
11956 /*
11957  * Based on:
11958  * Ext JS Library 1.1.1
11959  * Copyright(c) 2006-2007, Ext JS, LLC.
11960  *
11961  * Originally Released Under LGPL - original licence link has changed is not relivant.
11962  *
11963  * Fork - LGPL
11964  * <script type="text/javascript">
11965  */
11966
11967 /**
11968  * @class Roo.util.DelayedTask
11969  * Provides a convenient method of performing setTimeout where a new
11970  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11971  * You can use this class to buffer
11972  * the keypress events for a certain number of milliseconds, and perform only if they stop
11973  * for that amount of time.
11974  * @constructor The parameters to this constructor serve as defaults and are not required.
11975  * @param {Function} fn (optional) The default function to timeout
11976  * @param {Object} scope (optional) The default scope of that timeout
11977  * @param {Array} args (optional) The default Array of arguments
11978  */
11979 Roo.util.DelayedTask = function(fn, scope, args){
11980     var id = null, d, t;
11981
11982     var call = function(){
11983         var now = new Date().getTime();
11984         if(now - t >= d){
11985             clearInterval(id);
11986             id = null;
11987             fn.apply(scope, args || []);
11988         }
11989     };
11990     /**
11991      * Cancels any pending timeout and queues a new one
11992      * @param {Number} delay The milliseconds to delay
11993      * @param {Function} newFn (optional) Overrides function passed to constructor
11994      * @param {Object} newScope (optional) Overrides scope passed to constructor
11995      * @param {Array} newArgs (optional) Overrides args passed to constructor
11996      */
11997     this.delay = function(delay, newFn, newScope, newArgs){
11998         if(id && delay != d){
11999             this.cancel();
12000         }
12001         d = delay;
12002         t = new Date().getTime();
12003         fn = newFn || fn;
12004         scope = newScope || scope;
12005         args = newArgs || args;
12006         if(!id){
12007             id = setInterval(call, d);
12008         }
12009     };
12010
12011     /**
12012      * Cancel the last queued timeout
12013      */
12014     this.cancel = function(){
12015         if(id){
12016             clearInterval(id);
12017             id = null;
12018         }
12019     };
12020 };/*
12021  * Based on:
12022  * Ext JS Library 1.1.1
12023  * Copyright(c) 2006-2007, Ext JS, LLC.
12024  *
12025  * Originally Released Under LGPL - original licence link has changed is not relivant.
12026  *
12027  * Fork - LGPL
12028  * <script type="text/javascript">
12029  */
12030  
12031  
12032 Roo.util.TaskRunner = function(interval){
12033     interval = interval || 10;
12034     var tasks = [], removeQueue = [];
12035     var id = 0;
12036     var running = false;
12037
12038     var stopThread = function(){
12039         running = false;
12040         clearInterval(id);
12041         id = 0;
12042     };
12043
12044     var startThread = function(){
12045         if(!running){
12046             running = true;
12047             id = setInterval(runTasks, interval);
12048         }
12049     };
12050
12051     var removeTask = function(task){
12052         removeQueue.push(task);
12053         if(task.onStop){
12054             task.onStop();
12055         }
12056     };
12057
12058     var runTasks = function(){
12059         if(removeQueue.length > 0){
12060             for(var i = 0, len = removeQueue.length; i < len; i++){
12061                 tasks.remove(removeQueue[i]);
12062             }
12063             removeQueue = [];
12064             if(tasks.length < 1){
12065                 stopThread();
12066                 return;
12067             }
12068         }
12069         var now = new Date().getTime();
12070         for(var i = 0, len = tasks.length; i < len; ++i){
12071             var t = tasks[i];
12072             var itime = now - t.taskRunTime;
12073             if(t.interval <= itime){
12074                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12075                 t.taskRunTime = now;
12076                 if(rt === false || t.taskRunCount === t.repeat){
12077                     removeTask(t);
12078                     return;
12079                 }
12080             }
12081             if(t.duration && t.duration <= (now - t.taskStartTime)){
12082                 removeTask(t);
12083             }
12084         }
12085     };
12086
12087     /**
12088      * Queues a new task.
12089      * @param {Object} task
12090      */
12091     this.start = function(task){
12092         tasks.push(task);
12093         task.taskStartTime = new Date().getTime();
12094         task.taskRunTime = 0;
12095         task.taskRunCount = 0;
12096         startThread();
12097         return task;
12098     };
12099
12100     this.stop = function(task){
12101         removeTask(task);
12102         return task;
12103     };
12104
12105     this.stopAll = function(){
12106         stopThread();
12107         for(var i = 0, len = tasks.length; i < len; i++){
12108             if(tasks[i].onStop){
12109                 tasks[i].onStop();
12110             }
12111         }
12112         tasks = [];
12113         removeQueue = [];
12114     };
12115 };
12116
12117 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127
12128  
12129 /**
12130  * @class Roo.util.MixedCollection
12131  * @extends Roo.util.Observable
12132  * A Collection class that maintains both numeric indexes and keys and exposes events.
12133  * @constructor
12134  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12135  * collection (defaults to false)
12136  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12137  * and return the key value for that item.  This is used when available to look up the key on items that
12138  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12139  * equivalent to providing an implementation for the {@link #getKey} method.
12140  */
12141 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12142     this.items = [];
12143     this.map = {};
12144     this.keys = [];
12145     this.length = 0;
12146     this.addEvents({
12147         /**
12148          * @event clear
12149          * Fires when the collection is cleared.
12150          */
12151         "clear" : true,
12152         /**
12153          * @event add
12154          * Fires when an item is added to the collection.
12155          * @param {Number} index The index at which the item was added.
12156          * @param {Object} o The item added.
12157          * @param {String} key The key associated with the added item.
12158          */
12159         "add" : true,
12160         /**
12161          * @event replace
12162          * Fires when an item is replaced in the collection.
12163          * @param {String} key he key associated with the new added.
12164          * @param {Object} old The item being replaced.
12165          * @param {Object} new The new item.
12166          */
12167         "replace" : true,
12168         /**
12169          * @event remove
12170          * Fires when an item is removed from the collection.
12171          * @param {Object} o The item being removed.
12172          * @param {String} key (optional) The key associated with the removed item.
12173          */
12174         "remove" : true,
12175         "sort" : true
12176     });
12177     this.allowFunctions = allowFunctions === true;
12178     if(keyFn){
12179         this.getKey = keyFn;
12180     }
12181     Roo.util.MixedCollection.superclass.constructor.call(this);
12182 };
12183
12184 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12185     allowFunctions : false,
12186     
12187 /**
12188  * Adds an item to the collection.
12189  * @param {String} key The key to associate with the item
12190  * @param {Object} o The item to add.
12191  * @return {Object} The item added.
12192  */
12193     add : function(key, o){
12194         if(arguments.length == 1){
12195             o = arguments[0];
12196             key = this.getKey(o);
12197         }
12198         if(typeof key == "undefined" || key === null){
12199             this.length++;
12200             this.items.push(o);
12201             this.keys.push(null);
12202         }else{
12203             var old = this.map[key];
12204             if(old){
12205                 return this.replace(key, o);
12206             }
12207             this.length++;
12208             this.items.push(o);
12209             this.map[key] = o;
12210             this.keys.push(key);
12211         }
12212         this.fireEvent("add", this.length-1, o, key);
12213         return o;
12214     },
12215    
12216 /**
12217   * MixedCollection has a generic way to fetch keys if you implement getKey.
12218 <pre><code>
12219 // normal way
12220 var mc = new Roo.util.MixedCollection();
12221 mc.add(someEl.dom.id, someEl);
12222 mc.add(otherEl.dom.id, otherEl);
12223 //and so on
12224
12225 // using getKey
12226 var mc = new Roo.util.MixedCollection();
12227 mc.getKey = function(el){
12228    return el.dom.id;
12229 };
12230 mc.add(someEl);
12231 mc.add(otherEl);
12232
12233 // or via the constructor
12234 var mc = new Roo.util.MixedCollection(false, function(el){
12235    return el.dom.id;
12236 });
12237 mc.add(someEl);
12238 mc.add(otherEl);
12239 </code></pre>
12240  * @param o {Object} The item for which to find the key.
12241  * @return {Object} The key for the passed item.
12242  */
12243     getKey : function(o){
12244          return o.id; 
12245     },
12246    
12247 /**
12248  * Replaces an item in the collection.
12249  * @param {String} key The key associated with the item to replace, or the item to replace.
12250  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12251  * @return {Object}  The new item.
12252  */
12253     replace : function(key, o){
12254         if(arguments.length == 1){
12255             o = arguments[0];
12256             key = this.getKey(o);
12257         }
12258         var old = this.item(key);
12259         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12260              return this.add(key, o);
12261         }
12262         var index = this.indexOfKey(key);
12263         this.items[index] = o;
12264         this.map[key] = o;
12265         this.fireEvent("replace", key, old, o);
12266         return o;
12267     },
12268    
12269 /**
12270  * Adds all elements of an Array or an Object to the collection.
12271  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12272  * an Array of values, each of which are added to the collection.
12273  */
12274     addAll : function(objs){
12275         if(arguments.length > 1 || objs instanceof Array){
12276             var args = arguments.length > 1 ? arguments : objs;
12277             for(var i = 0, len = args.length; i < len; i++){
12278                 this.add(args[i]);
12279             }
12280         }else{
12281             for(var key in objs){
12282                 if(this.allowFunctions || typeof objs[key] != "function"){
12283                     this.add(key, objs[key]);
12284                 }
12285             }
12286         }
12287     },
12288    
12289 /**
12290  * Executes the specified function once for every item in the collection, passing each
12291  * item as the first and only parameter. returning false from the function will stop the iteration.
12292  * @param {Function} fn The function to execute for each item.
12293  * @param {Object} scope (optional) The scope in which to execute the function.
12294  */
12295     each : function(fn, scope){
12296         var items = [].concat(this.items); // each safe for removal
12297         for(var i = 0, len = items.length; i < len; i++){
12298             if(fn.call(scope || items[i], items[i], i, len) === false){
12299                 break;
12300             }
12301         }
12302     },
12303    
12304 /**
12305  * Executes the specified function once for every key in the collection, passing each
12306  * key, and its associated item as the first two parameters.
12307  * @param {Function} fn The function to execute for each item.
12308  * @param {Object} scope (optional) The scope in which to execute the function.
12309  */
12310     eachKey : function(fn, scope){
12311         for(var i = 0, len = this.keys.length; i < len; i++){
12312             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12313         }
12314     },
12315    
12316 /**
12317  * Returns the first item in the collection which elicits a true return value from the
12318  * passed selection function.
12319  * @param {Function} fn The selection function to execute for each item.
12320  * @param {Object} scope (optional) The scope in which to execute the function.
12321  * @return {Object} The first item in the collection which returned true from the selection function.
12322  */
12323     find : function(fn, scope){
12324         for(var i = 0, len = this.items.length; i < len; i++){
12325             if(fn.call(scope || window, this.items[i], this.keys[i])){
12326                 return this.items[i];
12327             }
12328         }
12329         return null;
12330     },
12331    
12332 /**
12333  * Inserts an item at the specified index in the collection.
12334  * @param {Number} index The index to insert the item at.
12335  * @param {String} key The key to associate with the new item, or the item itself.
12336  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12337  * @return {Object} The item inserted.
12338  */
12339     insert : function(index, key, o){
12340         if(arguments.length == 2){
12341             o = arguments[1];
12342             key = this.getKey(o);
12343         }
12344         if(index >= this.length){
12345             return this.add(key, o);
12346         }
12347         this.length++;
12348         this.items.splice(index, 0, o);
12349         if(typeof key != "undefined" && key != null){
12350             this.map[key] = o;
12351         }
12352         this.keys.splice(index, 0, key);
12353         this.fireEvent("add", index, o, key);
12354         return o;
12355     },
12356    
12357 /**
12358  * Removed an item from the collection.
12359  * @param {Object} o The item to remove.
12360  * @return {Object} The item removed.
12361  */
12362     remove : function(o){
12363         return this.removeAt(this.indexOf(o));
12364     },
12365    
12366 /**
12367  * Remove an item from a specified index in the collection.
12368  * @param {Number} index The index within the collection of the item to remove.
12369  */
12370     removeAt : function(index){
12371         if(index < this.length && index >= 0){
12372             this.length--;
12373             var o = this.items[index];
12374             this.items.splice(index, 1);
12375             var key = this.keys[index];
12376             if(typeof key != "undefined"){
12377                 delete this.map[key];
12378             }
12379             this.keys.splice(index, 1);
12380             this.fireEvent("remove", o, key);
12381         }
12382     },
12383    
12384 /**
12385  * Removed an item associated with the passed key fom the collection.
12386  * @param {String} key The key of the item to remove.
12387  */
12388     removeKey : function(key){
12389         return this.removeAt(this.indexOfKey(key));
12390     },
12391    
12392 /**
12393  * Returns the number of items in the collection.
12394  * @return {Number} the number of items in the collection.
12395  */
12396     getCount : function(){
12397         return this.length; 
12398     },
12399    
12400 /**
12401  * Returns index within the collection of the passed Object.
12402  * @param {Object} o The item to find the index of.
12403  * @return {Number} index of the item.
12404  */
12405     indexOf : function(o){
12406         if(!this.items.indexOf){
12407             for(var i = 0, len = this.items.length; i < len; i++){
12408                 if(this.items[i] == o) return i;
12409             }
12410             return -1;
12411         }else{
12412             return this.items.indexOf(o);
12413         }
12414     },
12415    
12416 /**
12417  * Returns index within the collection of the passed key.
12418  * @param {String} key The key to find the index of.
12419  * @return {Number} index of the key.
12420  */
12421     indexOfKey : function(key){
12422         if(!this.keys.indexOf){
12423             for(var i = 0, len = this.keys.length; i < len; i++){
12424                 if(this.keys[i] == key) return i;
12425             }
12426             return -1;
12427         }else{
12428             return this.keys.indexOf(key);
12429         }
12430     },
12431    
12432 /**
12433  * Returns the item associated with the passed key OR index. Key has priority over index.
12434  * @param {String/Number} key The key or index of the item.
12435  * @return {Object} The item associated with the passed key.
12436  */
12437     item : function(key){
12438         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12439         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12440     },
12441     
12442 /**
12443  * Returns the item at the specified index.
12444  * @param {Number} index The index of the item.
12445  * @return {Object}
12446  */
12447     itemAt : function(index){
12448         return this.items[index];
12449     },
12450     
12451 /**
12452  * Returns the item associated with the passed key.
12453  * @param {String/Number} key The key of the item.
12454  * @return {Object} The item associated with the passed key.
12455  */
12456     key : function(key){
12457         return this.map[key];
12458     },
12459    
12460 /**
12461  * Returns true if the collection contains the passed Object as an item.
12462  * @param {Object} o  The Object to look for in the collection.
12463  * @return {Boolean} True if the collection contains the Object as an item.
12464  */
12465     contains : function(o){
12466         return this.indexOf(o) != -1;
12467     },
12468    
12469 /**
12470  * Returns true if the collection contains the passed Object as a key.
12471  * @param {String} key The key to look for in the collection.
12472  * @return {Boolean} True if the collection contains the Object as a key.
12473  */
12474     containsKey : function(key){
12475         return typeof this.map[key] != "undefined";
12476     },
12477    
12478 /**
12479  * Removes all items from the collection.
12480  */
12481     clear : function(){
12482         this.length = 0;
12483         this.items = [];
12484         this.keys = [];
12485         this.map = {};
12486         this.fireEvent("clear");
12487     },
12488    
12489 /**
12490  * Returns the first item in the collection.
12491  * @return {Object} the first item in the collection..
12492  */
12493     first : function(){
12494         return this.items[0]; 
12495     },
12496    
12497 /**
12498  * Returns the last item in the collection.
12499  * @return {Object} the last item in the collection..
12500  */
12501     last : function(){
12502         return this.items[this.length-1];   
12503     },
12504     
12505     _sort : function(property, dir, fn){
12506         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12507         fn = fn || function(a, b){
12508             return a-b;
12509         };
12510         var c = [], k = this.keys, items = this.items;
12511         for(var i = 0, len = items.length; i < len; i++){
12512             c[c.length] = {key: k[i], value: items[i], index: i};
12513         }
12514         c.sort(function(a, b){
12515             var v = fn(a[property], b[property]) * dsc;
12516             if(v == 0){
12517                 v = (a.index < b.index ? -1 : 1);
12518             }
12519             return v;
12520         });
12521         for(var i = 0, len = c.length; i < len; i++){
12522             items[i] = c[i].value;
12523             k[i] = c[i].key;
12524         }
12525         this.fireEvent("sort", this);
12526     },
12527     
12528     /**
12529      * Sorts this collection with the passed comparison function
12530      * @param {String} direction (optional) "ASC" or "DESC"
12531      * @param {Function} fn (optional) comparison function
12532      */
12533     sort : function(dir, fn){
12534         this._sort("value", dir, fn);
12535     },
12536     
12537     /**
12538      * Sorts this collection by keys
12539      * @param {String} direction (optional) "ASC" or "DESC"
12540      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12541      */
12542     keySort : function(dir, fn){
12543         this._sort("key", dir, fn || function(a, b){
12544             return String(a).toUpperCase()-String(b).toUpperCase();
12545         });
12546     },
12547     
12548     /**
12549      * Returns a range of items in this collection
12550      * @param {Number} startIndex (optional) defaults to 0
12551      * @param {Number} endIndex (optional) default to the last item
12552      * @return {Array} An array of items
12553      */
12554     getRange : function(start, end){
12555         var items = this.items;
12556         if(items.length < 1){
12557             return [];
12558         }
12559         start = start || 0;
12560         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12561         var r = [];
12562         if(start <= end){
12563             for(var i = start; i <= end; i++) {
12564                     r[r.length] = items[i];
12565             }
12566         }else{
12567             for(var i = start; i >= end; i--) {
12568                     r[r.length] = items[i];
12569             }
12570         }
12571         return r;
12572     },
12573         
12574     /**
12575      * Filter the <i>objects</i> in this collection by a specific property. 
12576      * Returns a new collection that has been filtered.
12577      * @param {String} property A property on your objects
12578      * @param {String/RegExp} value Either string that the property values 
12579      * should start with or a RegExp to test against the property
12580      * @return {MixedCollection} The new filtered collection
12581      */
12582     filter : function(property, value){
12583         if(!value.exec){ // not a regex
12584             value = String(value);
12585             if(value.length == 0){
12586                 return this.clone();
12587             }
12588             value = new RegExp("^" + Roo.escapeRe(value), "i");
12589         }
12590         return this.filterBy(function(o){
12591             return o && value.test(o[property]);
12592         });
12593         },
12594     
12595     /**
12596      * Filter by a function. * Returns a new collection that has been filtered.
12597      * The passed function will be called with each 
12598      * object in the collection. If the function returns true, the value is included 
12599      * otherwise it is filtered.
12600      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12601      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12602      * @return {MixedCollection} The new filtered collection
12603      */
12604     filterBy : function(fn, scope){
12605         var r = new Roo.util.MixedCollection();
12606         r.getKey = this.getKey;
12607         var k = this.keys, it = this.items;
12608         for(var i = 0, len = it.length; i < len; i++){
12609             if(fn.call(scope||this, it[i], k[i])){
12610                                 r.add(k[i], it[i]);
12611                         }
12612         }
12613         return r;
12614     },
12615     
12616     /**
12617      * Creates a duplicate of this collection
12618      * @return {MixedCollection}
12619      */
12620     clone : function(){
12621         var r = new Roo.util.MixedCollection();
12622         var k = this.keys, it = this.items;
12623         for(var i = 0, len = it.length; i < len; i++){
12624             r.add(k[i], it[i]);
12625         }
12626         r.getKey = this.getKey;
12627         return r;
12628     }
12629 });
12630 /**
12631  * Returns the item associated with the passed key or index.
12632  * @method
12633  * @param {String/Number} key The key or index of the item.
12634  * @return {Object} The item associated with the passed key.
12635  */
12636 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12637  * Based on:
12638  * Ext JS Library 1.1.1
12639  * Copyright(c) 2006-2007, Ext JS, LLC.
12640  *
12641  * Originally Released Under LGPL - original licence link has changed is not relivant.
12642  *
12643  * Fork - LGPL
12644  * <script type="text/javascript">
12645  */
12646 /**
12647  * @class Roo.util.JSON
12648  * Modified version of Douglas Crockford"s json.js that doesn"t
12649  * mess with the Object prototype 
12650  * http://www.json.org/js.html
12651  * @singleton
12652  */
12653 Roo.util.JSON = new (function(){
12654     var useHasOwn = {}.hasOwnProperty ? true : false;
12655     
12656     // crashes Safari in some instances
12657     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12658     
12659     var pad = function(n) {
12660         return n < 10 ? "0" + n : n;
12661     };
12662     
12663     var m = {
12664         "\b": '\\b',
12665         "\t": '\\t',
12666         "\n": '\\n',
12667         "\f": '\\f',
12668         "\r": '\\r',
12669         '"' : '\\"',
12670         "\\": '\\\\'
12671     };
12672
12673     var encodeString = function(s){
12674         if (/["\\\x00-\x1f]/.test(s)) {
12675             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12676                 var c = m[b];
12677                 if(c){
12678                     return c;
12679                 }
12680                 c = b.charCodeAt();
12681                 return "\\u00" +
12682                     Math.floor(c / 16).toString(16) +
12683                     (c % 16).toString(16);
12684             }) + '"';
12685         }
12686         return '"' + s + '"';
12687     };
12688     
12689     var encodeArray = function(o){
12690         var a = ["["], b, i, l = o.length, v;
12691             for (i = 0; i < l; i += 1) {
12692                 v = o[i];
12693                 switch (typeof v) {
12694                     case "undefined":
12695                     case "function":
12696                     case "unknown":
12697                         break;
12698                     default:
12699                         if (b) {
12700                             a.push(',');
12701                         }
12702                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12703                         b = true;
12704                 }
12705             }
12706             a.push("]");
12707             return a.join("");
12708     };
12709     
12710     var encodeDate = function(o){
12711         return '"' + o.getFullYear() + "-" +
12712                 pad(o.getMonth() + 1) + "-" +
12713                 pad(o.getDate()) + "T" +
12714                 pad(o.getHours()) + ":" +
12715                 pad(o.getMinutes()) + ":" +
12716                 pad(o.getSeconds()) + '"';
12717     };
12718     
12719     /**
12720      * Encodes an Object, Array or other value
12721      * @param {Mixed} o The variable to encode
12722      * @return {String} The JSON string
12723      */
12724     this.encode = function(o){
12725         if(typeof o == "undefined" || o === null){
12726             return "null";
12727         }else if(o instanceof Array){
12728             return encodeArray(o);
12729         }else if(o instanceof Date){
12730             return encodeDate(o);
12731         }else if(typeof o == "string"){
12732             return encodeString(o);
12733         }else if(typeof o == "number"){
12734             return isFinite(o) ? String(o) : "null";
12735         }else if(typeof o == "boolean"){
12736             return String(o);
12737         }else {
12738             var a = ["{"], b, i, v;
12739             for (i in o) {
12740                 if(!useHasOwn || o.hasOwnProperty(i)) {
12741                     v = o[i];
12742                     switch (typeof v) {
12743                     case "undefined":
12744                     case "function":
12745                     case "unknown":
12746                         break;
12747                     default:
12748                         if(b){
12749                             a.push(',');
12750                         }
12751                         a.push(this.encode(i), ":",
12752                                 v === null ? "null" : this.encode(v));
12753                         b = true;
12754                     }
12755                 }
12756             }
12757             a.push("}");
12758             return a.join("");
12759         }
12760     };
12761     
12762     /**
12763      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12764      * @param {String} json The JSON string
12765      * @return {Object} The resulting object
12766      */
12767     this.decode = function(json){
12768         /**
12769          * eval:var:json
12770          */
12771         return eval("(" + json + ')');
12772     };
12773 })();
12774 /** 
12775  * Shorthand for {@link Roo.util.JSON#encode}
12776  * @member Roo encode 
12777  * @method */
12778 Roo.encode = Roo.util.JSON.encode;
12779 /** 
12780  * Shorthand for {@link Roo.util.JSON#decode}
12781  * @member Roo decode 
12782  * @method */
12783 Roo.decode = Roo.util.JSON.decode;
12784 /*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794  
12795 /**
12796  * @class Roo.util.Format
12797  * Reusable data formatting functions
12798  * @singleton
12799  */
12800 Roo.util.Format = function(){
12801     var trimRe = /^\s+|\s+$/g;
12802     return {
12803         /**
12804          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12805          * @param {String} value The string to truncate
12806          * @param {Number} length The maximum length to allow before truncating
12807          * @return {String} The converted text
12808          */
12809         ellipsis : function(value, len){
12810             if(value && value.length > len){
12811                 return value.substr(0, len-3)+"...";
12812             }
12813             return value;
12814         },
12815
12816         /**
12817          * Checks a reference and converts it to empty string if it is undefined
12818          * @param {Mixed} value Reference to check
12819          * @return {Mixed} Empty string if converted, otherwise the original value
12820          */
12821         undef : function(value){
12822             return typeof value != "undefined" ? value : "";
12823         },
12824
12825         /**
12826          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12827          * @param {String} value The string to encode
12828          * @return {String} The encoded text
12829          */
12830         htmlEncode : function(value){
12831             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12832         },
12833
12834         /**
12835          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12836          * @param {String} value The string to decode
12837          * @return {String} The decoded text
12838          */
12839         htmlDecode : function(value){
12840             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12841         },
12842
12843         /**
12844          * Trims any whitespace from either side of a string
12845          * @param {String} value The text to trim
12846          * @return {String} The trimmed text
12847          */
12848         trim : function(value){
12849             return String(value).replace(trimRe, "");
12850         },
12851
12852         /**
12853          * Returns a substring from within an original string
12854          * @param {String} value The original text
12855          * @param {Number} start The start index of the substring
12856          * @param {Number} length The length of the substring
12857          * @return {String} The substring
12858          */
12859         substr : function(value, start, length){
12860             return String(value).substr(start, length);
12861         },
12862
12863         /**
12864          * Converts a string to all lower case letters
12865          * @param {String} value The text to convert
12866          * @return {String} The converted text
12867          */
12868         lowercase : function(value){
12869             return String(value).toLowerCase();
12870         },
12871
12872         /**
12873          * Converts a string to all upper case letters
12874          * @param {String} value The text to convert
12875          * @return {String} The converted text
12876          */
12877         uppercase : function(value){
12878             return String(value).toUpperCase();
12879         },
12880
12881         /**
12882          * Converts the first character only of a string to upper case
12883          * @param {String} value The text to convert
12884          * @return {String} The converted text
12885          */
12886         capitalize : function(value){
12887             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12888         },
12889
12890         // private
12891         call : function(value, fn){
12892             if(arguments.length > 2){
12893                 var args = Array.prototype.slice.call(arguments, 2);
12894                 args.unshift(value);
12895                  
12896                 return /** eval:var:value */  eval(fn).apply(window, args);
12897             }else{
12898                 /** eval:var:value */
12899                 return /** eval:var:value */ eval(fn).call(window, value);
12900             }
12901         },
12902
12903         /**
12904          * Format a number as US currency
12905          * @param {Number/String} value The numeric value to format
12906          * @return {String} The formatted currency string
12907          */
12908         usMoney : function(v){
12909             v = (Math.round((v-0)*100))/100;
12910             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12911             v = String(v);
12912             var ps = v.split('.');
12913             var whole = ps[0];
12914             var sub = ps[1] ? '.'+ ps[1] : '.00';
12915             var r = /(\d+)(\d{3})/;
12916             while (r.test(whole)) {
12917                 whole = whole.replace(r, '$1' + ',' + '$2');
12918             }
12919             return "$" + whole + sub ;
12920         },
12921
12922         /**
12923          * Parse a value into a formatted date using the specified format pattern.
12924          * @param {Mixed} value The value to format
12925          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12926          * @return {String} The formatted date string
12927          */
12928         date : function(v, format){
12929             if(!v){
12930                 return "";
12931             }
12932             if(!(v instanceof Date)){
12933                 v = new Date(Date.parse(v));
12934             }
12935             return v.dateFormat(format || "m/d/Y");
12936         },
12937
12938         /**
12939          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12940          * @param {String} format Any valid date format string
12941          * @return {Function} The date formatting function
12942          */
12943         dateRenderer : function(format){
12944             return function(v){
12945                 return Roo.util.Format.date(v, format);  
12946             };
12947         },
12948
12949         // private
12950         stripTagsRE : /<\/?[^>]+>/gi,
12951         
12952         /**
12953          * Strips all HTML tags
12954          * @param {Mixed} value The text from which to strip tags
12955          * @return {String} The stripped text
12956          */
12957         stripTags : function(v){
12958             return !v ? v : String(v).replace(this.stripTagsRE, "");
12959         }
12960     };
12961 }();/*
12962  * Based on:
12963  * Ext JS Library 1.1.1
12964  * Copyright(c) 2006-2007, Ext JS, LLC.
12965  *
12966  * Originally Released Under LGPL - original licence link has changed is not relivant.
12967  *
12968  * Fork - LGPL
12969  * <script type="text/javascript">
12970  */
12971
12972
12973  
12974
12975 /**
12976  * @class Roo.MasterTemplate
12977  * @extends Roo.Template
12978  * Provides a template that can have child templates. The syntax is:
12979 <pre><code>
12980 var t = new Roo.MasterTemplate(
12981         '&lt;select name="{name}"&gt;',
12982                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12983         '&lt;/select&gt;'
12984 );
12985 t.add('options', {value: 'foo', text: 'bar'});
12986 // or you can add multiple child elements in one shot
12987 t.addAll('options', [
12988     {value: 'foo', text: 'bar'},
12989     {value: 'foo2', text: 'bar2'},
12990     {value: 'foo3', text: 'bar3'}
12991 ]);
12992 // then append, applying the master template values
12993 t.append('my-form', {name: 'my-select'});
12994 </code></pre>
12995 * A name attribute for the child template is not required if you have only one child
12996 * template or you want to refer to them by index.
12997  */
12998 Roo.MasterTemplate = function(){
12999     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13000     this.originalHtml = this.html;
13001     var st = {};
13002     var m, re = this.subTemplateRe;
13003     re.lastIndex = 0;
13004     var subIndex = 0;
13005     while(m = re.exec(this.html)){
13006         var name = m[1], content = m[2];
13007         st[subIndex] = {
13008             name: name,
13009             index: subIndex,
13010             buffer: [],
13011             tpl : new Roo.Template(content)
13012         };
13013         if(name){
13014             st[name] = st[subIndex];
13015         }
13016         st[subIndex].tpl.compile();
13017         st[subIndex].tpl.call = this.call.createDelegate(this);
13018         subIndex++;
13019     }
13020     this.subCount = subIndex;
13021     this.subs = st;
13022 };
13023 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13024     /**
13025     * The regular expression used to match sub templates
13026     * @type RegExp
13027     * @property
13028     */
13029     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13030
13031     /**
13032      * Applies the passed values to a child template.
13033      * @param {String/Number} name (optional) The name or index of the child template
13034      * @param {Array/Object} values The values to be applied to the template
13035      * @return {MasterTemplate} this
13036      */
13037      add : function(name, values){
13038         if(arguments.length == 1){
13039             values = arguments[0];
13040             name = 0;
13041         }
13042         var s = this.subs[name];
13043         s.buffer[s.buffer.length] = s.tpl.apply(values);
13044         return this;
13045     },
13046
13047     /**
13048      * Applies all the passed values to a child template.
13049      * @param {String/Number} name (optional) The name or index of the child template
13050      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13051      * @param {Boolean} reset (optional) True to reset the template first
13052      * @return {MasterTemplate} this
13053      */
13054     fill : function(name, values, reset){
13055         var a = arguments;
13056         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13057             values = a[0];
13058             name = 0;
13059             reset = a[1];
13060         }
13061         if(reset){
13062             this.reset();
13063         }
13064         for(var i = 0, len = values.length; i < len; i++){
13065             this.add(name, values[i]);
13066         }
13067         return this;
13068     },
13069
13070     /**
13071      * Resets the template for reuse
13072      * @return {MasterTemplate} this
13073      */
13074      reset : function(){
13075         var s = this.subs;
13076         for(var i = 0; i < this.subCount; i++){
13077             s[i].buffer = [];
13078         }
13079         return this;
13080     },
13081
13082     applyTemplate : function(values){
13083         var s = this.subs;
13084         var replaceIndex = -1;
13085         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13086             return s[++replaceIndex].buffer.join("");
13087         });
13088         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13089     },
13090
13091     apply : function(){
13092         return this.applyTemplate.apply(this, arguments);
13093     },
13094
13095     compile : function(){return this;}
13096 });
13097
13098 /**
13099  * Alias for fill().
13100  * @method
13101  */
13102 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13103  /**
13104  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13105  * var tpl = Roo.MasterTemplate.from('element-id');
13106  * @param {String/HTMLElement} el
13107  * @param {Object} config
13108  * @static
13109  */
13110 Roo.MasterTemplate.from = function(el, config){
13111     el = Roo.getDom(el);
13112     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13113 };/*
13114  * Based on:
13115  * Ext JS Library 1.1.1
13116  * Copyright(c) 2006-2007, Ext JS, LLC.
13117  *
13118  * Originally Released Under LGPL - original licence link has changed is not relivant.
13119  *
13120  * Fork - LGPL
13121  * <script type="text/javascript">
13122  */
13123
13124  
13125 /**
13126  * @class Roo.util.CSS
13127  * Utility class for manipulating CSS rules
13128  * @singleton
13129  */
13130 Roo.util.CSS = function(){
13131         var rules = null;
13132         var doc = document;
13133
13134     var camelRe = /(-[a-z])/gi;
13135     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13136
13137    return {
13138    /**
13139     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13140     * tag and appended to the HEAD of the document.
13141     * @param {String} cssText The text containing the css rules
13142     * @param {String} id An id to add to the stylesheet for later removal
13143     * @return {StyleSheet}
13144     */
13145    createStyleSheet : function(cssText, id){
13146        var ss;
13147        var head = doc.getElementsByTagName("head")[0];
13148        var rules = doc.createElement("style");
13149        rules.setAttribute("type", "text/css");
13150        if(id){
13151            rules.setAttribute("id", id);
13152        }
13153        if(Roo.isIE){
13154            head.appendChild(rules);
13155            ss = rules.styleSheet;
13156            ss.cssText = cssText;
13157        }else{
13158            try{
13159                 rules.appendChild(doc.createTextNode(cssText));
13160            }catch(e){
13161                rules.cssText = cssText; 
13162            }
13163            head.appendChild(rules);
13164            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13165        }
13166        this.cacheStyleSheet(ss);
13167        return ss;
13168    },
13169
13170    /**
13171     * Removes a style or link tag by id
13172     * @param {String} id The id of the tag
13173     */
13174    removeStyleSheet : function(id){
13175        var existing = doc.getElementById(id);
13176        if(existing){
13177            existing.parentNode.removeChild(existing);
13178        }
13179    },
13180
13181    /**
13182     * Dynamically swaps an existing stylesheet reference for a new one
13183     * @param {String} id The id of an existing link tag to remove
13184     * @param {String} url The href of the new stylesheet to include
13185     */
13186    swapStyleSheet : function(id, url){
13187        this.removeStyleSheet(id);
13188        var ss = doc.createElement("link");
13189        ss.setAttribute("rel", "stylesheet");
13190        ss.setAttribute("type", "text/css");
13191        ss.setAttribute("id", id);
13192        ss.setAttribute("href", url);
13193        doc.getElementsByTagName("head")[0].appendChild(ss);
13194    },
13195    
13196    /**
13197     * Refresh the rule cache if you have dynamically added stylesheets
13198     * @return {Object} An object (hash) of rules indexed by selector
13199     */
13200    refreshCache : function(){
13201        return this.getRules(true);
13202    },
13203
13204    // private
13205    cacheStyleSheet : function(ss){
13206        if(!rules){
13207            rules = {};
13208        }
13209        try{// try catch for cross domain access issue
13210            var ssRules = ss.cssRules || ss.rules;
13211            for(var j = ssRules.length-1; j >= 0; --j){
13212                rules[ssRules[j].selectorText] = ssRules[j];
13213            }
13214        }catch(e){}
13215    },
13216    
13217    /**
13218     * Gets all css rules for the document
13219     * @param {Boolean} refreshCache true to refresh the internal cache
13220     * @return {Object} An object (hash) of rules indexed by selector
13221     */
13222    getRules : function(refreshCache){
13223                 if(rules == null || refreshCache){
13224                         rules = {};
13225                         var ds = doc.styleSheets;
13226                         for(var i =0, len = ds.length; i < len; i++){
13227                             try{
13228                         this.cacheStyleSheet(ds[i]);
13229                     }catch(e){} 
13230                 }
13231                 }
13232                 return rules;
13233         },
13234         
13235         /**
13236     * Gets an an individual CSS rule by selector(s)
13237     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13238     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13239     * @return {CSSRule} The CSS rule or null if one is not found
13240     */
13241    getRule : function(selector, refreshCache){
13242                 var rs = this.getRules(refreshCache);
13243                 if(!(selector instanceof Array)){
13244                     return rs[selector];
13245                 }
13246                 for(var i = 0; i < selector.length; i++){
13247                         if(rs[selector[i]]){
13248                                 return rs[selector[i]];
13249                         }
13250                 }
13251                 return null;
13252         },
13253         
13254         
13255         /**
13256     * Updates a rule property
13257     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13258     * @param {String} property The css property
13259     * @param {String} value The new value for the property
13260     * @return {Boolean} true If a rule was found and updated
13261     */
13262    updateRule : function(selector, property, value){
13263                 if(!(selector instanceof Array)){
13264                         var rule = this.getRule(selector);
13265                         if(rule){
13266                                 rule.style[property.replace(camelRe, camelFn)] = value;
13267                                 return true;
13268                         }
13269                 }else{
13270                         for(var i = 0; i < selector.length; i++){
13271                                 if(this.updateRule(selector[i], property, value)){
13272                                         return true;
13273                                 }
13274                         }
13275                 }
13276                 return false;
13277         }
13278    };   
13279 }();/*
13280  * Based on:
13281  * Ext JS Library 1.1.1
13282  * Copyright(c) 2006-2007, Ext JS, LLC.
13283  *
13284  * Originally Released Under LGPL - original licence link has changed is not relivant.
13285  *
13286  * Fork - LGPL
13287  * <script type="text/javascript">
13288  */
13289
13290  
13291
13292 /**
13293  * @class Roo.util.ClickRepeater
13294  * @extends Roo.util.Observable
13295  * 
13296  * A wrapper class which can be applied to any element. Fires a "click" event while the
13297  * mouse is pressed. The interval between firings may be specified in the config but
13298  * defaults to 10 milliseconds.
13299  * 
13300  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13301  * 
13302  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13303  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13304  * Similar to an autorepeat key delay.
13305  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13306  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13307  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13308  *           "interval" and "delay" are ignored. "immediate" is honored.
13309  * @cfg {Boolean} preventDefault True to prevent the default click event
13310  * @cfg {Boolean} stopDefault True to stop the default click event
13311  * 
13312  * @history
13313  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13314  *     2007-02-02 jvs Renamed to ClickRepeater
13315  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13316  *
13317  *  @constructor
13318  * @param {String/HTMLElement/Element} el The element to listen on
13319  * @param {Object} config
13320  **/
13321 Roo.util.ClickRepeater = function(el, config)
13322 {
13323     this.el = Roo.get(el);
13324     this.el.unselectable();
13325
13326     Roo.apply(this, config);
13327
13328     this.addEvents({
13329     /**
13330      * @event mousedown
13331      * Fires when the mouse button is depressed.
13332      * @param {Roo.util.ClickRepeater} this
13333      */
13334         "mousedown" : true,
13335     /**
13336      * @event click
13337      * Fires on a specified interval during the time the element is pressed.
13338      * @param {Roo.util.ClickRepeater} this
13339      */
13340         "click" : true,
13341     /**
13342      * @event mouseup
13343      * Fires when the mouse key is released.
13344      * @param {Roo.util.ClickRepeater} this
13345      */
13346         "mouseup" : true
13347     });
13348
13349     this.el.on("mousedown", this.handleMouseDown, this);
13350     if(this.preventDefault || this.stopDefault){
13351         this.el.on("click", function(e){
13352             if(this.preventDefault){
13353                 e.preventDefault();
13354             }
13355             if(this.stopDefault){
13356                 e.stopEvent();
13357             }
13358         }, this);
13359     }
13360
13361     // allow inline handler
13362     if(this.handler){
13363         this.on("click", this.handler,  this.scope || this);
13364     }
13365
13366     Roo.util.ClickRepeater.superclass.constructor.call(this);
13367 };
13368
13369 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13370     interval : 20,
13371     delay: 250,
13372     preventDefault : true,
13373     stopDefault : false,
13374     timer : 0,
13375
13376     // private
13377     handleMouseDown : function(){
13378         clearTimeout(this.timer);
13379         this.el.blur();
13380         if(this.pressClass){
13381             this.el.addClass(this.pressClass);
13382         }
13383         this.mousedownTime = new Date();
13384
13385         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13386         this.el.on("mouseout", this.handleMouseOut, this);
13387
13388         this.fireEvent("mousedown", this);
13389         this.fireEvent("click", this);
13390         
13391         this.timer = this.click.defer(this.delay || this.interval, this);
13392     },
13393
13394     // private
13395     click : function(){
13396         this.fireEvent("click", this);
13397         this.timer = this.click.defer(this.getInterval(), this);
13398     },
13399
13400     // private
13401     getInterval: function(){
13402         if(!this.accelerate){
13403             return this.interval;
13404         }
13405         var pressTime = this.mousedownTime.getElapsed();
13406         if(pressTime < 500){
13407             return 400;
13408         }else if(pressTime < 1700){
13409             return 320;
13410         }else if(pressTime < 2600){
13411             return 250;
13412         }else if(pressTime < 3500){
13413             return 180;
13414         }else if(pressTime < 4400){
13415             return 140;
13416         }else if(pressTime < 5300){
13417             return 80;
13418         }else if(pressTime < 6200){
13419             return 50;
13420         }else{
13421             return 10;
13422         }
13423     },
13424
13425     // private
13426     handleMouseOut : function(){
13427         clearTimeout(this.timer);
13428         if(this.pressClass){
13429             this.el.removeClass(this.pressClass);
13430         }
13431         this.el.on("mouseover", this.handleMouseReturn, this);
13432     },
13433
13434     // private
13435     handleMouseReturn : function(){
13436         this.el.un("mouseover", this.handleMouseReturn);
13437         if(this.pressClass){
13438             this.el.addClass(this.pressClass);
13439         }
13440         this.click();
13441     },
13442
13443     // private
13444     handleMouseUp : function(){
13445         clearTimeout(this.timer);
13446         this.el.un("mouseover", this.handleMouseReturn);
13447         this.el.un("mouseout", this.handleMouseOut);
13448         Roo.get(document).un("mouseup", this.handleMouseUp);
13449         this.el.removeClass(this.pressClass);
13450         this.fireEvent("mouseup", this);
13451     }
13452 });/*
13453  * Based on:
13454  * Ext JS Library 1.1.1
13455  * Copyright(c) 2006-2007, Ext JS, LLC.
13456  *
13457  * Originally Released Under LGPL - original licence link has changed is not relivant.
13458  *
13459  * Fork - LGPL
13460  * <script type="text/javascript">
13461  */
13462
13463  
13464 /**
13465  * @class Roo.KeyNav
13466  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13467  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13468  * way to implement custom navigation schemes for any UI component.</p>
13469  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13470  * pageUp, pageDown, del, home, end.  Usage:</p>
13471  <pre><code>
13472 var nav = new Roo.KeyNav("my-element", {
13473     "left" : function(e){
13474         this.moveLeft(e.ctrlKey);
13475     },
13476     "right" : function(e){
13477         this.moveRight(e.ctrlKey);
13478     },
13479     "enter" : function(e){
13480         this.save();
13481     },
13482     scope : this
13483 });
13484 </code></pre>
13485  * @constructor
13486  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13487  * @param {Object} config The config
13488  */
13489 Roo.KeyNav = function(el, config){
13490     this.el = Roo.get(el);
13491     Roo.apply(this, config);
13492     if(!this.disabled){
13493         this.disabled = true;
13494         this.enable();
13495     }
13496 };
13497
13498 Roo.KeyNav.prototype = {
13499     /**
13500      * @cfg {Boolean} disabled
13501      * True to disable this KeyNav instance (defaults to false)
13502      */
13503     disabled : false,
13504     /**
13505      * @cfg {String} defaultEventAction
13506      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13507      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13508      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13509      */
13510     defaultEventAction: "stopEvent",
13511     /**
13512      * @cfg {Boolean} forceKeyDown
13513      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13514      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13515      * handle keydown instead of keypress.
13516      */
13517     forceKeyDown : false,
13518
13519     // private
13520     prepareEvent : function(e){
13521         var k = e.getKey();
13522         var h = this.keyToHandler[k];
13523         //if(h && this[h]){
13524         //    e.stopPropagation();
13525         //}
13526         if(Roo.isSafari && h && k >= 37 && k <= 40){
13527             e.stopEvent();
13528         }
13529     },
13530
13531     // private
13532     relay : function(e){
13533         var k = e.getKey();
13534         var h = this.keyToHandler[k];
13535         if(h && this[h]){
13536             if(this.doRelay(e, this[h], h) !== true){
13537                 e[this.defaultEventAction]();
13538             }
13539         }
13540     },
13541
13542     // private
13543     doRelay : function(e, h, hname){
13544         return h.call(this.scope || this, e);
13545     },
13546
13547     // possible handlers
13548     enter : false,
13549     left : false,
13550     right : false,
13551     up : false,
13552     down : false,
13553     tab : false,
13554     esc : false,
13555     pageUp : false,
13556     pageDown : false,
13557     del : false,
13558     home : false,
13559     end : false,
13560
13561     // quick lookup hash
13562     keyToHandler : {
13563         37 : "left",
13564         39 : "right",
13565         38 : "up",
13566         40 : "down",
13567         33 : "pageUp",
13568         34 : "pageDown",
13569         46 : "del",
13570         36 : "home",
13571         35 : "end",
13572         13 : "enter",
13573         27 : "esc",
13574         9  : "tab"
13575     },
13576
13577         /**
13578          * Enable this KeyNav
13579          */
13580         enable: function(){
13581                 if(this.disabled){
13582             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13583             // the EventObject will normalize Safari automatically
13584             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13585                 this.el.on("keydown", this.relay,  this);
13586             }else{
13587                 this.el.on("keydown", this.prepareEvent,  this);
13588                 this.el.on("keypress", this.relay,  this);
13589             }
13590                     this.disabled = false;
13591                 }
13592         },
13593
13594         /**
13595          * Disable this KeyNav
13596          */
13597         disable: function(){
13598                 if(!this.disabled){
13599                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13600                 this.el.un("keydown", this.relay);
13601             }else{
13602                 this.el.un("keydown", this.prepareEvent);
13603                 this.el.un("keypress", this.relay);
13604             }
13605                     this.disabled = true;
13606                 }
13607         }
13608 };/*
13609  * Based on:
13610  * Ext JS Library 1.1.1
13611  * Copyright(c) 2006-2007, Ext JS, LLC.
13612  *
13613  * Originally Released Under LGPL - original licence link has changed is not relivant.
13614  *
13615  * Fork - LGPL
13616  * <script type="text/javascript">
13617  */
13618
13619  
13620 /**
13621  * @class Roo.KeyMap
13622  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13623  * The constructor accepts the same config object as defined by {@link #addBinding}.
13624  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13625  * combination it will call the function with this signature (if the match is a multi-key
13626  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13627  * A KeyMap can also handle a string representation of keys.<br />
13628  * Usage:
13629  <pre><code>
13630 // map one key by key code
13631 var map = new Roo.KeyMap("my-element", {
13632     key: 13, // or Roo.EventObject.ENTER
13633     fn: myHandler,
13634     scope: myObject
13635 });
13636
13637 // map multiple keys to one action by string
13638 var map = new Roo.KeyMap("my-element", {
13639     key: "a\r\n\t",
13640     fn: myHandler,
13641     scope: myObject
13642 });
13643
13644 // map multiple keys to multiple actions by strings and array of codes
13645 var map = new Roo.KeyMap("my-element", [
13646     {
13647         key: [10,13],
13648         fn: function(){ alert("Return was pressed"); }
13649     }, {
13650         key: "abc",
13651         fn: function(){ alert('a, b or c was pressed'); }
13652     }, {
13653         key: "\t",
13654         ctrl:true,
13655         shift:true,
13656         fn: function(){ alert('Control + shift + tab was pressed.'); }
13657     }
13658 ]);
13659 </code></pre>
13660  * <b>Note: A KeyMap starts enabled</b>
13661  * @constructor
13662  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13663  * @param {Object} config The config (see {@link #addBinding})
13664  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13665  */
13666 Roo.KeyMap = function(el, config, eventName){
13667     this.el  = Roo.get(el);
13668     this.eventName = eventName || "keydown";
13669     this.bindings = [];
13670     if(config){
13671         this.addBinding(config);
13672     }
13673     this.enable();
13674 };
13675
13676 Roo.KeyMap.prototype = {
13677     /**
13678      * True to stop the event from bubbling and prevent the default browser action if the
13679      * key was handled by the KeyMap (defaults to false)
13680      * @type Boolean
13681      */
13682     stopEvent : false,
13683
13684     /**
13685      * Add a new binding to this KeyMap. The following config object properties are supported:
13686      * <pre>
13687 Property    Type             Description
13688 ----------  ---------------  ----------------------------------------------------------------------
13689 key         String/Array     A single keycode or an array of keycodes to handle
13690 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13691 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13692 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13693 fn          Function         The function to call when KeyMap finds the expected key combination
13694 scope       Object           The scope of the callback function
13695 </pre>
13696      *
13697      * Usage:
13698      * <pre><code>
13699 // Create a KeyMap
13700 var map = new Roo.KeyMap(document, {
13701     key: Roo.EventObject.ENTER,
13702     fn: handleKey,
13703     scope: this
13704 });
13705
13706 //Add a new binding to the existing KeyMap later
13707 map.addBinding({
13708     key: 'abc',
13709     shift: true,
13710     fn: handleKey,
13711     scope: this
13712 });
13713 </code></pre>
13714      * @param {Object/Array} config A single KeyMap config or an array of configs
13715      */
13716         addBinding : function(config){
13717         if(config instanceof Array){
13718             for(var i = 0, len = config.length; i < len; i++){
13719                 this.addBinding(config[i]);
13720             }
13721             return;
13722         }
13723         var keyCode = config.key,
13724             shift = config.shift, 
13725             ctrl = config.ctrl, 
13726             alt = config.alt,
13727             fn = config.fn,
13728             scope = config.scope;
13729         if(typeof keyCode == "string"){
13730             var ks = [];
13731             var keyString = keyCode.toUpperCase();
13732             for(var j = 0, len = keyString.length; j < len; j++){
13733                 ks.push(keyString.charCodeAt(j));
13734             }
13735             keyCode = ks;
13736         }
13737         var keyArray = keyCode instanceof Array;
13738         var handler = function(e){
13739             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13740                 var k = e.getKey();
13741                 if(keyArray){
13742                     for(var i = 0, len = keyCode.length; i < len; i++){
13743                         if(keyCode[i] == k){
13744                           if(this.stopEvent){
13745                               e.stopEvent();
13746                           }
13747                           fn.call(scope || window, k, e);
13748                           return;
13749                         }
13750                     }
13751                 }else{
13752                     if(k == keyCode){
13753                         if(this.stopEvent){
13754                            e.stopEvent();
13755                         }
13756                         fn.call(scope || window, k, e);
13757                     }
13758                 }
13759             }
13760         };
13761         this.bindings.push(handler);  
13762         },
13763
13764     /**
13765      * Shorthand for adding a single key listener
13766      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13767      * following options:
13768      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13769      * @param {Function} fn The function to call
13770      * @param {Object} scope (optional) The scope of the function
13771      */
13772     on : function(key, fn, scope){
13773         var keyCode, shift, ctrl, alt;
13774         if(typeof key == "object" && !(key instanceof Array)){
13775             keyCode = key.key;
13776             shift = key.shift;
13777             ctrl = key.ctrl;
13778             alt = key.alt;
13779         }else{
13780             keyCode = key;
13781         }
13782         this.addBinding({
13783             key: keyCode,
13784             shift: shift,
13785             ctrl: ctrl,
13786             alt: alt,
13787             fn: fn,
13788             scope: scope
13789         })
13790     },
13791
13792     // private
13793     handleKeyDown : function(e){
13794             if(this.enabled){ //just in case
13795             var b = this.bindings;
13796             for(var i = 0, len = b.length; i < len; i++){
13797                 b[i].call(this, e);
13798             }
13799             }
13800         },
13801         
13802         /**
13803          * Returns true if this KeyMap is enabled
13804          * @return {Boolean} 
13805          */
13806         isEnabled : function(){
13807             return this.enabled;  
13808         },
13809         
13810         /**
13811          * Enables this KeyMap
13812          */
13813         enable: function(){
13814                 if(!this.enabled){
13815                     this.el.on(this.eventName, this.handleKeyDown, this);
13816                     this.enabled = true;
13817                 }
13818         },
13819
13820         /**
13821          * Disable this KeyMap
13822          */
13823         disable: function(){
13824                 if(this.enabled){
13825                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13826                     this.enabled = false;
13827                 }
13828         }
13829 };/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840  
13841 /**
13842  * @class Roo.util.TextMetrics
13843  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13844  * wide, in pixels, a given block of text will be.
13845  * @singleton
13846  */
13847 Roo.util.TextMetrics = function(){
13848     var shared;
13849     return {
13850         /**
13851          * Measures the size of the specified text
13852          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13853          * that can affect the size of the rendered text
13854          * @param {String} text The text to measure
13855          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13856          * in order to accurately measure the text height
13857          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13858          */
13859         measure : function(el, text, fixedWidth){
13860             if(!shared){
13861                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13862             }
13863             shared.bind(el);
13864             shared.setFixedWidth(fixedWidth || 'auto');
13865             return shared.getSize(text);
13866         },
13867
13868         /**
13869          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13870          * the overhead of multiple calls to initialize the style properties on each measurement.
13871          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13872          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13873          * in order to accurately measure the text height
13874          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13875          */
13876         createInstance : function(el, fixedWidth){
13877             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13878         }
13879     };
13880 }();
13881
13882  
13883
13884 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13885     var ml = new Roo.Element(document.createElement('div'));
13886     document.body.appendChild(ml.dom);
13887     ml.position('absolute');
13888     ml.setLeftTop(-1000, -1000);
13889     ml.hide();
13890
13891     if(fixedWidth){
13892         ml.setWidth(fixedWidth);
13893     }
13894      
13895     var instance = {
13896         /**
13897          * Returns the size of the specified text based on the internal element's style and width properties
13898          * @memberOf Roo.util.TextMetrics.Instance#
13899          * @param {String} text The text to measure
13900          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13901          */
13902         getSize : function(text){
13903             ml.update(text);
13904             var s = ml.getSize();
13905             ml.update('');
13906             return s;
13907         },
13908
13909         /**
13910          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13911          * that can affect the size of the rendered text
13912          * @memberOf Roo.util.TextMetrics.Instance#
13913          * @param {String/HTMLElement} el The element, dom node or id
13914          */
13915         bind : function(el){
13916             ml.setStyle(
13917                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13918             );
13919         },
13920
13921         /**
13922          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13923          * to set a fixed width in order to accurately measure the text height.
13924          * @memberOf Roo.util.TextMetrics.Instance#
13925          * @param {Number} width The width to set on the element
13926          */
13927         setFixedWidth : function(width){
13928             ml.setWidth(width);
13929         },
13930
13931         /**
13932          * Returns the measured width of the specified text
13933          * @memberOf Roo.util.TextMetrics.Instance#
13934          * @param {String} text The text to measure
13935          * @return {Number} width The width in pixels
13936          */
13937         getWidth : function(text){
13938             ml.dom.style.width = 'auto';
13939             return this.getSize(text).width;
13940         },
13941
13942         /**
13943          * Returns the measured height of the specified text.  For multiline text, be sure to call
13944          * {@link #setFixedWidth} if necessary.
13945          * @memberOf Roo.util.TextMetrics.Instance#
13946          * @param {String} text The text to measure
13947          * @return {Number} height The height in pixels
13948          */
13949         getHeight : function(text){
13950             return this.getSize(text).height;
13951         }
13952     };
13953
13954     instance.bind(bindTo);
13955
13956     return instance;
13957 };
13958
13959 // backwards compat
13960 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13961  * Based on:
13962  * Ext JS Library 1.1.1
13963  * Copyright(c) 2006-2007, Ext JS, LLC.
13964  *
13965  * Originally Released Under LGPL - original licence link has changed is not relivant.
13966  *
13967  * Fork - LGPL
13968  * <script type="text/javascript">
13969  */
13970
13971 /**
13972  * @class Roo.state.Provider
13973  * Abstract base class for state provider implementations. This class provides methods
13974  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13975  * Provider interface.
13976  */
13977 Roo.state.Provider = function(){
13978     /**
13979      * @event statechange
13980      * Fires when a state change occurs.
13981      * @param {Provider} this This state provider
13982      * @param {String} key The state key which was changed
13983      * @param {String} value The encoded value for the state
13984      */
13985     this.addEvents({
13986         "statechange": true
13987     });
13988     this.state = {};
13989     Roo.state.Provider.superclass.constructor.call(this);
13990 };
13991 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13992     /**
13993      * Returns the current value for a key
13994      * @param {String} name The key name
13995      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13996      * @return {Mixed} The state data
13997      */
13998     get : function(name, defaultValue){
13999         return typeof this.state[name] == "undefined" ?
14000             defaultValue : this.state[name];
14001     },
14002     
14003     /**
14004      * Clears a value from the state
14005      * @param {String} name The key name
14006      */
14007     clear : function(name){
14008         delete this.state[name];
14009         this.fireEvent("statechange", this, name, null);
14010     },
14011     
14012     /**
14013      * Sets the value for a key
14014      * @param {String} name The key name
14015      * @param {Mixed} value The value to set
14016      */
14017     set : function(name, value){
14018         this.state[name] = value;
14019         this.fireEvent("statechange", this, name, value);
14020     },
14021     
14022     /**
14023      * Decodes a string previously encoded with {@link #encodeValue}.
14024      * @param {String} value The value to decode
14025      * @return {Mixed} The decoded value
14026      */
14027     decodeValue : function(cookie){
14028         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14029         var matches = re.exec(unescape(cookie));
14030         if(!matches || !matches[1]) return; // non state cookie
14031         var type = matches[1];
14032         var v = matches[2];
14033         switch(type){
14034             case "n":
14035                 return parseFloat(v);
14036             case "d":
14037                 return new Date(Date.parse(v));
14038             case "b":
14039                 return (v == "1");
14040             case "a":
14041                 var all = [];
14042                 var values = v.split("^");
14043                 for(var i = 0, len = values.length; i < len; i++){
14044                     all.push(this.decodeValue(values[i]));
14045                 }
14046                 return all;
14047            case "o":
14048                 var all = {};
14049                 var values = v.split("^");
14050                 for(var i = 0, len = values.length; i < len; i++){
14051                     var kv = values[i].split("=");
14052                     all[kv[0]] = this.decodeValue(kv[1]);
14053                 }
14054                 return all;
14055            default:
14056                 return v;
14057         }
14058     },
14059     
14060     /**
14061      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14062      * @param {Mixed} value The value to encode
14063      * @return {String} The encoded value
14064      */
14065     encodeValue : function(v){
14066         var enc;
14067         if(typeof v == "number"){
14068             enc = "n:" + v;
14069         }else if(typeof v == "boolean"){
14070             enc = "b:" + (v ? "1" : "0");
14071         }else if(v instanceof Date){
14072             enc = "d:" + v.toGMTString();
14073         }else if(v instanceof Array){
14074             var flat = "";
14075             for(var i = 0, len = v.length; i < len; i++){
14076                 flat += this.encodeValue(v[i]);
14077                 if(i != len-1) flat += "^";
14078             }
14079             enc = "a:" + flat;
14080         }else if(typeof v == "object"){
14081             var flat = "";
14082             for(var key in v){
14083                 if(typeof v[key] != "function"){
14084                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14085                 }
14086             }
14087             enc = "o:" + flat.substring(0, flat.length-1);
14088         }else{
14089             enc = "s:" + v;
14090         }
14091         return escape(enc);        
14092     }
14093 });
14094
14095 /*
14096  * Based on:
14097  * Ext JS Library 1.1.1
14098  * Copyright(c) 2006-2007, Ext JS, LLC.
14099  *
14100  * Originally Released Under LGPL - original licence link has changed is not relivant.
14101  *
14102  * Fork - LGPL
14103  * <script type="text/javascript">
14104  */
14105 /**
14106  * @class Roo.state.Manager
14107  * This is the global state manager. By default all components that are "state aware" check this class
14108  * for state information if you don't pass them a custom state provider. In order for this class
14109  * to be useful, it must be initialized with a provider when your application initializes.
14110  <pre><code>
14111 // in your initialization function
14112 init : function(){
14113    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14114    ...
14115    // supposed you have a {@link Roo.BorderLayout}
14116    var layout = new Roo.BorderLayout(...);
14117    layout.restoreState();
14118    // or a {Roo.BasicDialog}
14119    var dialog = new Roo.BasicDialog(...);
14120    dialog.restoreState();
14121  </code></pre>
14122  * @singleton
14123  */
14124 Roo.state.Manager = function(){
14125     var provider = new Roo.state.Provider();
14126     
14127     return {
14128         /**
14129          * Configures the default state provider for your application
14130          * @param {Provider} stateProvider The state provider to set
14131          */
14132         setProvider : function(stateProvider){
14133             provider = stateProvider;
14134         },
14135         
14136         /**
14137          * Returns the current value for a key
14138          * @param {String} name The key name
14139          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14140          * @return {Mixed} The state data
14141          */
14142         get : function(key, defaultValue){
14143             return provider.get(key, defaultValue);
14144         },
14145         
14146         /**
14147          * Sets the value for a key
14148          * @param {String} name The key name
14149          * @param {Mixed} value The state data
14150          */
14151          set : function(key, value){
14152             provider.set(key, value);
14153         },
14154         
14155         /**
14156          * Clears a value from the state
14157          * @param {String} name The key name
14158          */
14159         clear : function(key){
14160             provider.clear(key);
14161         },
14162         
14163         /**
14164          * Gets the currently configured state provider
14165          * @return {Provider} The state provider
14166          */
14167         getProvider : function(){
14168             return provider;
14169         }
14170     };
14171 }();
14172 /*
14173  * Based on:
14174  * Ext JS Library 1.1.1
14175  * Copyright(c) 2006-2007, Ext JS, LLC.
14176  *
14177  * Originally Released Under LGPL - original licence link has changed is not relivant.
14178  *
14179  * Fork - LGPL
14180  * <script type="text/javascript">
14181  */
14182 /**
14183  * @class Roo.state.CookieProvider
14184  * @extends Roo.state.Provider
14185  * The default Provider implementation which saves state via cookies.
14186  * <br />Usage:
14187  <pre><code>
14188    var cp = new Roo.state.CookieProvider({
14189        path: "/cgi-bin/",
14190        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14191        domain: "roojs.com"
14192    })
14193    Roo.state.Manager.setProvider(cp);
14194  </code></pre>
14195  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14196  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14197  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14198  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14199  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14200  * domain the page is running on including the 'www' like 'www.roojs.com')
14201  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14202  * @constructor
14203  * Create a new CookieProvider
14204  * @param {Object} config The configuration object
14205  */
14206 Roo.state.CookieProvider = function(config){
14207     Roo.state.CookieProvider.superclass.constructor.call(this);
14208     this.path = "/";
14209     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14210     this.domain = null;
14211     this.secure = false;
14212     Roo.apply(this, config);
14213     this.state = this.readCookies();
14214 };
14215
14216 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14217     // private
14218     set : function(name, value){
14219         if(typeof value == "undefined" || value === null){
14220             this.clear(name);
14221             return;
14222         }
14223         this.setCookie(name, value);
14224         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14225     },
14226
14227     // private
14228     clear : function(name){
14229         this.clearCookie(name);
14230         Roo.state.CookieProvider.superclass.clear.call(this, name);
14231     },
14232
14233     // private
14234     readCookies : function(){
14235         var cookies = {};
14236         var c = document.cookie + ";";
14237         var re = /\s?(.*?)=(.*?);/g;
14238         var matches;
14239         while((matches = re.exec(c)) != null){
14240             var name = matches[1];
14241             var value = matches[2];
14242             if(name && name.substring(0,3) == "ys-"){
14243                 cookies[name.substr(3)] = this.decodeValue(value);
14244             }
14245         }
14246         return cookies;
14247     },
14248
14249     // private
14250     setCookie : function(name, value){
14251         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14252            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14253            ((this.path == null) ? "" : ("; path=" + this.path)) +
14254            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14255            ((this.secure == true) ? "; secure" : "");
14256     },
14257
14258     // private
14259     clearCookie : function(name){
14260         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14261            ((this.path == null) ? "" : ("; path=" + this.path)) +
14262            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14263            ((this.secure == true) ? "; secure" : "");
14264     }
14265 });/*
14266  * Based on:
14267  * Ext JS Library 1.1.1
14268  * Copyright(c) 2006-2007, Ext JS, LLC.
14269  *
14270  * Originally Released Under LGPL - original licence link has changed is not relivant.
14271  *
14272  * Fork - LGPL
14273  * <script type="text/javascript">
14274  */
14275
14276
14277
14278 /*
14279  * These classes are derivatives of the similarly named classes in the YUI Library.
14280  * The original license:
14281  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14282  * Code licensed under the BSD License:
14283  * http://developer.yahoo.net/yui/license.txt
14284  */
14285
14286 (function() {
14287
14288 var Event=Roo.EventManager;
14289 var Dom=Roo.lib.Dom;
14290
14291 /**
14292  * @class Roo.dd.DragDrop
14293  * Defines the interface and base operation of items that that can be
14294  * dragged or can be drop targets.  It was designed to be extended, overriding
14295  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14296  * Up to three html elements can be associated with a DragDrop instance:
14297  * <ul>
14298  * <li>linked element: the element that is passed into the constructor.
14299  * This is the element which defines the boundaries for interaction with
14300  * other DragDrop objects.</li>
14301  * <li>handle element(s): The drag operation only occurs if the element that
14302  * was clicked matches a handle element.  By default this is the linked
14303  * element, but there are times that you will want only a portion of the
14304  * linked element to initiate the drag operation, and the setHandleElId()
14305  * method provides a way to define this.</li>
14306  * <li>drag element: this represents the element that would be moved along
14307  * with the cursor during a drag operation.  By default, this is the linked
14308  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14309  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14310  * </li>
14311  * </ul>
14312  * This class should not be instantiated until the onload event to ensure that
14313  * the associated elements are available.
14314  * The following would define a DragDrop obj that would interact with any
14315  * other DragDrop obj in the "group1" group:
14316  * <pre>
14317  *  dd = new Roo.dd.DragDrop("div1", "group1");
14318  * </pre>
14319  * Since none of the event handlers have been implemented, nothing would
14320  * actually happen if you were to run the code above.  Normally you would
14321  * override this class or one of the default implementations, but you can
14322  * also override the methods you want on an instance of the class...
14323  * <pre>
14324  *  dd.onDragDrop = function(e, id) {
14325  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14326  *  }
14327  * </pre>
14328  * @constructor
14329  * @param {String} id of the element that is linked to this instance
14330  * @param {String} sGroup the group of related DragDrop objects
14331  * @param {object} config an object containing configurable attributes
14332  *                Valid properties for DragDrop:
14333  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14334  */
14335 Roo.dd.DragDrop = function(id, sGroup, config) {
14336     if (id) {
14337         this.init(id, sGroup, config);
14338     }
14339 };
14340
14341 Roo.dd.DragDrop.prototype = {
14342
14343     /**
14344      * The id of the element associated with this object.  This is what we
14345      * refer to as the "linked element" because the size and position of
14346      * this element is used to determine when the drag and drop objects have
14347      * interacted.
14348      * @property id
14349      * @type String
14350      */
14351     id: null,
14352
14353     /**
14354      * Configuration attributes passed into the constructor
14355      * @property config
14356      * @type object
14357      */
14358     config: null,
14359
14360     /**
14361      * The id of the element that will be dragged.  By default this is same
14362      * as the linked element , but could be changed to another element. Ex:
14363      * Roo.dd.DDProxy
14364      * @property dragElId
14365      * @type String
14366      * @private
14367      */
14368     dragElId: null,
14369
14370     /**
14371      * the id of the element that initiates the drag operation.  By default
14372      * this is the linked element, but could be changed to be a child of this
14373      * element.  This lets us do things like only starting the drag when the
14374      * header element within the linked html element is clicked.
14375      * @property handleElId
14376      * @type String
14377      * @private
14378      */
14379     handleElId: null,
14380
14381     /**
14382      * An associative array of HTML tags that will be ignored if clicked.
14383      * @property invalidHandleTypes
14384      * @type {string: string}
14385      */
14386     invalidHandleTypes: null,
14387
14388     /**
14389      * An associative array of ids for elements that will be ignored if clicked
14390      * @property invalidHandleIds
14391      * @type {string: string}
14392      */
14393     invalidHandleIds: null,
14394
14395     /**
14396      * An indexted array of css class names for elements that will be ignored
14397      * if clicked.
14398      * @property invalidHandleClasses
14399      * @type string[]
14400      */
14401     invalidHandleClasses: null,
14402
14403     /**
14404      * The linked element's absolute X position at the time the drag was
14405      * started
14406      * @property startPageX
14407      * @type int
14408      * @private
14409      */
14410     startPageX: 0,
14411
14412     /**
14413      * The linked element's absolute X position at the time the drag was
14414      * started
14415      * @property startPageY
14416      * @type int
14417      * @private
14418      */
14419     startPageY: 0,
14420
14421     /**
14422      * The group defines a logical collection of DragDrop objects that are
14423      * related.  Instances only get events when interacting with other
14424      * DragDrop object in the same group.  This lets us define multiple
14425      * groups using a single DragDrop subclass if we want.
14426      * @property groups
14427      * @type {string: string}
14428      */
14429     groups: null,
14430
14431     /**
14432      * Individual drag/drop instances can be locked.  This will prevent
14433      * onmousedown start drag.
14434      * @property locked
14435      * @type boolean
14436      * @private
14437      */
14438     locked: false,
14439
14440     /**
14441      * Lock this instance
14442      * @method lock
14443      */
14444     lock: function() { this.locked = true; },
14445
14446     /**
14447      * Unlock this instace
14448      * @method unlock
14449      */
14450     unlock: function() { this.locked = false; },
14451
14452     /**
14453      * By default, all insances can be a drop target.  This can be disabled by
14454      * setting isTarget to false.
14455      * @method isTarget
14456      * @type boolean
14457      */
14458     isTarget: true,
14459
14460     /**
14461      * The padding configured for this drag and drop object for calculating
14462      * the drop zone intersection with this object.
14463      * @method padding
14464      * @type int[]
14465      */
14466     padding: null,
14467
14468     /**
14469      * Cached reference to the linked element
14470      * @property _domRef
14471      * @private
14472      */
14473     _domRef: null,
14474
14475     /**
14476      * Internal typeof flag
14477      * @property __ygDragDrop
14478      * @private
14479      */
14480     __ygDragDrop: true,
14481
14482     /**
14483      * Set to true when horizontal contraints are applied
14484      * @property constrainX
14485      * @type boolean
14486      * @private
14487      */
14488     constrainX: false,
14489
14490     /**
14491      * Set to true when vertical contraints are applied
14492      * @property constrainY
14493      * @type boolean
14494      * @private
14495      */
14496     constrainY: false,
14497
14498     /**
14499      * The left constraint
14500      * @property minX
14501      * @type int
14502      * @private
14503      */
14504     minX: 0,
14505
14506     /**
14507      * The right constraint
14508      * @property maxX
14509      * @type int
14510      * @private
14511      */
14512     maxX: 0,
14513
14514     /**
14515      * The up constraint
14516      * @property minY
14517      * @type int
14518      * @type int
14519      * @private
14520      */
14521     minY: 0,
14522
14523     /**
14524      * The down constraint
14525      * @property maxY
14526      * @type int
14527      * @private
14528      */
14529     maxY: 0,
14530
14531     /**
14532      * Maintain offsets when we resetconstraints.  Set to true when you want
14533      * the position of the element relative to its parent to stay the same
14534      * when the page changes
14535      *
14536      * @property maintainOffset
14537      * @type boolean
14538      */
14539     maintainOffset: false,
14540
14541     /**
14542      * Array of pixel locations the element will snap to if we specified a
14543      * horizontal graduation/interval.  This array is generated automatically
14544      * when you define a tick interval.
14545      * @property xTicks
14546      * @type int[]
14547      */
14548     xTicks: null,
14549
14550     /**
14551      * Array of pixel locations the element will snap to if we specified a
14552      * vertical graduation/interval.  This array is generated automatically
14553      * when you define a tick interval.
14554      * @property yTicks
14555      * @type int[]
14556      */
14557     yTicks: null,
14558
14559     /**
14560      * By default the drag and drop instance will only respond to the primary
14561      * button click (left button for a right-handed mouse).  Set to true to
14562      * allow drag and drop to start with any mouse click that is propogated
14563      * by the browser
14564      * @property primaryButtonOnly
14565      * @type boolean
14566      */
14567     primaryButtonOnly: true,
14568
14569     /**
14570      * The availabe property is false until the linked dom element is accessible.
14571      * @property available
14572      * @type boolean
14573      */
14574     available: false,
14575
14576     /**
14577      * By default, drags can only be initiated if the mousedown occurs in the
14578      * region the linked element is.  This is done in part to work around a
14579      * bug in some browsers that mis-report the mousedown if the previous
14580      * mouseup happened outside of the window.  This property is set to true
14581      * if outer handles are defined.
14582      *
14583      * @property hasOuterHandles
14584      * @type boolean
14585      * @default false
14586      */
14587     hasOuterHandles: false,
14588
14589     /**
14590      * Code that executes immediately before the startDrag event
14591      * @method b4StartDrag
14592      * @private
14593      */
14594     b4StartDrag: function(x, y) { },
14595
14596     /**
14597      * Abstract method called after a drag/drop object is clicked
14598      * and the drag or mousedown time thresholds have beeen met.
14599      * @method startDrag
14600      * @param {int} X click location
14601      * @param {int} Y click location
14602      */
14603     startDrag: function(x, y) { /* override this */ },
14604
14605     /**
14606      * Code that executes immediately before the onDrag event
14607      * @method b4Drag
14608      * @private
14609      */
14610     b4Drag: function(e) { },
14611
14612     /**
14613      * Abstract method called during the onMouseMove event while dragging an
14614      * object.
14615      * @method onDrag
14616      * @param {Event} e the mousemove event
14617      */
14618     onDrag: function(e) { /* override this */ },
14619
14620     /**
14621      * Abstract method called when this element fist begins hovering over
14622      * another DragDrop obj
14623      * @method onDragEnter
14624      * @param {Event} e the mousemove event
14625      * @param {String|DragDrop[]} id In POINT mode, the element
14626      * id this is hovering over.  In INTERSECT mode, an array of one or more
14627      * dragdrop items being hovered over.
14628      */
14629     onDragEnter: function(e, id) { /* override this */ },
14630
14631     /**
14632      * Code that executes immediately before the onDragOver event
14633      * @method b4DragOver
14634      * @private
14635      */
14636     b4DragOver: function(e) { },
14637
14638     /**
14639      * Abstract method called when this element is hovering over another
14640      * DragDrop obj
14641      * @method onDragOver
14642      * @param {Event} e the mousemove event
14643      * @param {String|DragDrop[]} id In POINT mode, the element
14644      * id this is hovering over.  In INTERSECT mode, an array of dd items
14645      * being hovered over.
14646      */
14647     onDragOver: function(e, id) { /* override this */ },
14648
14649     /**
14650      * Code that executes immediately before the onDragOut event
14651      * @method b4DragOut
14652      * @private
14653      */
14654     b4DragOut: function(e) { },
14655
14656     /**
14657      * Abstract method called when we are no longer hovering over an element
14658      * @method onDragOut
14659      * @param {Event} e the mousemove event
14660      * @param {String|DragDrop[]} id In POINT mode, the element
14661      * id this was hovering over.  In INTERSECT mode, an array of dd items
14662      * that the mouse is no longer over.
14663      */
14664     onDragOut: function(e, id) { /* override this */ },
14665
14666     /**
14667      * Code that executes immediately before the onDragDrop event
14668      * @method b4DragDrop
14669      * @private
14670      */
14671     b4DragDrop: function(e) { },
14672
14673     /**
14674      * Abstract method called when this item is dropped on another DragDrop
14675      * obj
14676      * @method onDragDrop
14677      * @param {Event} e the mouseup event
14678      * @param {String|DragDrop[]} id In POINT mode, the element
14679      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14680      * was dropped on.
14681      */
14682     onDragDrop: function(e, id) { /* override this */ },
14683
14684     /**
14685      * Abstract method called when this item is dropped on an area with no
14686      * drop target
14687      * @method onInvalidDrop
14688      * @param {Event} e the mouseup event
14689      */
14690     onInvalidDrop: function(e) { /* override this */ },
14691
14692     /**
14693      * Code that executes immediately before the endDrag event
14694      * @method b4EndDrag
14695      * @private
14696      */
14697     b4EndDrag: function(e) { },
14698
14699     /**
14700      * Fired when we are done dragging the object
14701      * @method endDrag
14702      * @param {Event} e the mouseup event
14703      */
14704     endDrag: function(e) { /* override this */ },
14705
14706     /**
14707      * Code executed immediately before the onMouseDown event
14708      * @method b4MouseDown
14709      * @param {Event} e the mousedown event
14710      * @private
14711      */
14712     b4MouseDown: function(e) {  },
14713
14714     /**
14715      * Event handler that fires when a drag/drop obj gets a mousedown
14716      * @method onMouseDown
14717      * @param {Event} e the mousedown event
14718      */
14719     onMouseDown: function(e) { /* override this */ },
14720
14721     /**
14722      * Event handler that fires when a drag/drop obj gets a mouseup
14723      * @method onMouseUp
14724      * @param {Event} e the mouseup event
14725      */
14726     onMouseUp: function(e) { /* override this */ },
14727
14728     /**
14729      * Override the onAvailable method to do what is needed after the initial
14730      * position was determined.
14731      * @method onAvailable
14732      */
14733     onAvailable: function () {
14734     },
14735
14736     /*
14737      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14738      * @type Object
14739      */
14740     defaultPadding : {left:0, right:0, top:0, bottom:0},
14741
14742     /*
14743      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14744  *
14745  * Usage:
14746  <pre><code>
14747  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14748                 { dragElId: "existingProxyDiv" });
14749  dd.startDrag = function(){
14750      this.constrainTo("parent-id");
14751  };
14752  </code></pre>
14753  * Or you can initalize it using the {@link Roo.Element} object:
14754  <pre><code>
14755  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14756      startDrag : function(){
14757          this.constrainTo("parent-id");
14758      }
14759  });
14760  </code></pre>
14761      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14762      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14763      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14764      * an object containing the sides to pad. For example: {right:10, bottom:10}
14765      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14766      */
14767     constrainTo : function(constrainTo, pad, inContent){
14768         if(typeof pad == "number"){
14769             pad = {left: pad, right:pad, top:pad, bottom:pad};
14770         }
14771         pad = pad || this.defaultPadding;
14772         var b = Roo.get(this.getEl()).getBox();
14773         var ce = Roo.get(constrainTo);
14774         var s = ce.getScroll();
14775         var c, cd = ce.dom;
14776         if(cd == document.body){
14777             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14778         }else{
14779             xy = ce.getXY();
14780             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14781         }
14782
14783
14784         var topSpace = b.y - c.y;
14785         var leftSpace = b.x - c.x;
14786
14787         this.resetConstraints();
14788         this.setXConstraint(leftSpace - (pad.left||0), // left
14789                 c.width - leftSpace - b.width - (pad.right||0) //right
14790         );
14791         this.setYConstraint(topSpace - (pad.top||0), //top
14792                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14793         );
14794     },
14795
14796     /**
14797      * Returns a reference to the linked element
14798      * @method getEl
14799      * @return {HTMLElement} the html element
14800      */
14801     getEl: function() {
14802         if (!this._domRef) {
14803             this._domRef = Roo.getDom(this.id);
14804         }
14805
14806         return this._domRef;
14807     },
14808
14809     /**
14810      * Returns a reference to the actual element to drag.  By default this is
14811      * the same as the html element, but it can be assigned to another
14812      * element. An example of this can be found in Roo.dd.DDProxy
14813      * @method getDragEl
14814      * @return {HTMLElement} the html element
14815      */
14816     getDragEl: function() {
14817         return Roo.getDom(this.dragElId);
14818     },
14819
14820     /**
14821      * Sets up the DragDrop object.  Must be called in the constructor of any
14822      * Roo.dd.DragDrop subclass
14823      * @method init
14824      * @param id the id of the linked element
14825      * @param {String} sGroup the group of related items
14826      * @param {object} config configuration attributes
14827      */
14828     init: function(id, sGroup, config) {
14829         this.initTarget(id, sGroup, config);
14830         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14831         // Event.on(this.id, "selectstart", Event.preventDefault);
14832     },
14833
14834     /**
14835      * Initializes Targeting functionality only... the object does not
14836      * get a mousedown handler.
14837      * @method initTarget
14838      * @param id the id of the linked element
14839      * @param {String} sGroup the group of related items
14840      * @param {object} config configuration attributes
14841      */
14842     initTarget: function(id, sGroup, config) {
14843
14844         // configuration attributes
14845         this.config = config || {};
14846
14847         // create a local reference to the drag and drop manager
14848         this.DDM = Roo.dd.DDM;
14849         // initialize the groups array
14850         this.groups = {};
14851
14852         // assume that we have an element reference instead of an id if the
14853         // parameter is not a string
14854         if (typeof id !== "string") {
14855             id = Roo.id(id);
14856         }
14857
14858         // set the id
14859         this.id = id;
14860
14861         // add to an interaction group
14862         this.addToGroup((sGroup) ? sGroup : "default");
14863
14864         // We don't want to register this as the handle with the manager
14865         // so we just set the id rather than calling the setter.
14866         this.handleElId = id;
14867
14868         // the linked element is the element that gets dragged by default
14869         this.setDragElId(id);
14870
14871         // by default, clicked anchors will not start drag operations.
14872         this.invalidHandleTypes = { A: "A" };
14873         this.invalidHandleIds = {};
14874         this.invalidHandleClasses = [];
14875
14876         this.applyConfig();
14877
14878         this.handleOnAvailable();
14879     },
14880
14881     /**
14882      * Applies the configuration parameters that were passed into the constructor.
14883      * This is supposed to happen at each level through the inheritance chain.  So
14884      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14885      * DragDrop in order to get all of the parameters that are available in
14886      * each object.
14887      * @method applyConfig
14888      */
14889     applyConfig: function() {
14890
14891         // configurable properties:
14892         //    padding, isTarget, maintainOffset, primaryButtonOnly
14893         this.padding           = this.config.padding || [0, 0, 0, 0];
14894         this.isTarget          = (this.config.isTarget !== false);
14895         this.maintainOffset    = (this.config.maintainOffset);
14896         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14897
14898     },
14899
14900     /**
14901      * Executed when the linked element is available
14902      * @method handleOnAvailable
14903      * @private
14904      */
14905     handleOnAvailable: function() {
14906         this.available = true;
14907         this.resetConstraints();
14908         this.onAvailable();
14909     },
14910
14911      /**
14912      * Configures the padding for the target zone in px.  Effectively expands
14913      * (or reduces) the virtual object size for targeting calculations.
14914      * Supports css-style shorthand; if only one parameter is passed, all sides
14915      * will have that padding, and if only two are passed, the top and bottom
14916      * will have the first param, the left and right the second.
14917      * @method setPadding
14918      * @param {int} iTop    Top pad
14919      * @param {int} iRight  Right pad
14920      * @param {int} iBot    Bot pad
14921      * @param {int} iLeft   Left pad
14922      */
14923     setPadding: function(iTop, iRight, iBot, iLeft) {
14924         // this.padding = [iLeft, iRight, iTop, iBot];
14925         if (!iRight && 0 !== iRight) {
14926             this.padding = [iTop, iTop, iTop, iTop];
14927         } else if (!iBot && 0 !== iBot) {
14928             this.padding = [iTop, iRight, iTop, iRight];
14929         } else {
14930             this.padding = [iTop, iRight, iBot, iLeft];
14931         }
14932     },
14933
14934     /**
14935      * Stores the initial placement of the linked element.
14936      * @method setInitialPosition
14937      * @param {int} diffX   the X offset, default 0
14938      * @param {int} diffY   the Y offset, default 0
14939      */
14940     setInitPosition: function(diffX, diffY) {
14941         var el = this.getEl();
14942
14943         if (!this.DDM.verifyEl(el)) {
14944             return;
14945         }
14946
14947         var dx = diffX || 0;
14948         var dy = diffY || 0;
14949
14950         var p = Dom.getXY( el );
14951
14952         this.initPageX = p[0] - dx;
14953         this.initPageY = p[1] - dy;
14954
14955         this.lastPageX = p[0];
14956         this.lastPageY = p[1];
14957
14958
14959         this.setStartPosition(p);
14960     },
14961
14962     /**
14963      * Sets the start position of the element.  This is set when the obj
14964      * is initialized, the reset when a drag is started.
14965      * @method setStartPosition
14966      * @param pos current position (from previous lookup)
14967      * @private
14968      */
14969     setStartPosition: function(pos) {
14970         var p = pos || Dom.getXY( this.getEl() );
14971         this.deltaSetXY = null;
14972
14973         this.startPageX = p[0];
14974         this.startPageY = p[1];
14975     },
14976
14977     /**
14978      * Add this instance to a group of related drag/drop objects.  All
14979      * instances belong to at least one group, and can belong to as many
14980      * groups as needed.
14981      * @method addToGroup
14982      * @param sGroup {string} the name of the group
14983      */
14984     addToGroup: function(sGroup) {
14985         this.groups[sGroup] = true;
14986         this.DDM.regDragDrop(this, sGroup);
14987     },
14988
14989     /**
14990      * Remove's this instance from the supplied interaction group
14991      * @method removeFromGroup
14992      * @param {string}  sGroup  The group to drop
14993      */
14994     removeFromGroup: function(sGroup) {
14995         if (this.groups[sGroup]) {
14996             delete this.groups[sGroup];
14997         }
14998
14999         this.DDM.removeDDFromGroup(this, sGroup);
15000     },
15001
15002     /**
15003      * Allows you to specify that an element other than the linked element
15004      * will be moved with the cursor during a drag
15005      * @method setDragElId
15006      * @param id {string} the id of the element that will be used to initiate the drag
15007      */
15008     setDragElId: function(id) {
15009         this.dragElId = id;
15010     },
15011
15012     /**
15013      * Allows you to specify a child of the linked element that should be
15014      * used to initiate the drag operation.  An example of this would be if
15015      * you have a content div with text and links.  Clicking anywhere in the
15016      * content area would normally start the drag operation.  Use this method
15017      * to specify that an element inside of the content div is the element
15018      * that starts the drag operation.
15019      * @method setHandleElId
15020      * @param id {string} the id of the element that will be used to
15021      * initiate the drag.
15022      */
15023     setHandleElId: function(id) {
15024         if (typeof id !== "string") {
15025             id = Roo.id(id);
15026         }
15027         this.handleElId = id;
15028         this.DDM.regHandle(this.id, id);
15029     },
15030
15031     /**
15032      * Allows you to set an element outside of the linked element as a drag
15033      * handle
15034      * @method setOuterHandleElId
15035      * @param id the id of the element that will be used to initiate the drag
15036      */
15037     setOuterHandleElId: function(id) {
15038         if (typeof id !== "string") {
15039             id = Roo.id(id);
15040         }
15041         Event.on(id, "mousedown",
15042                 this.handleMouseDown, this);
15043         this.setHandleElId(id);
15044
15045         this.hasOuterHandles = true;
15046     },
15047
15048     /**
15049      * Remove all drag and drop hooks for this element
15050      * @method unreg
15051      */
15052     unreg: function() {
15053         Event.un(this.id, "mousedown",
15054                 this.handleMouseDown);
15055         this._domRef = null;
15056         this.DDM._remove(this);
15057     },
15058
15059     destroy : function(){
15060         this.unreg();
15061     },
15062
15063     /**
15064      * Returns true if this instance is locked, or the drag drop mgr is locked
15065      * (meaning that all drag/drop is disabled on the page.)
15066      * @method isLocked
15067      * @return {boolean} true if this obj or all drag/drop is locked, else
15068      * false
15069      */
15070     isLocked: function() {
15071         return (this.DDM.isLocked() || this.locked);
15072     },
15073
15074     /**
15075      * Fired when this object is clicked
15076      * @method handleMouseDown
15077      * @param {Event} e
15078      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15079      * @private
15080      */
15081     handleMouseDown: function(e, oDD){
15082         if (this.primaryButtonOnly && e.button != 0) {
15083             return;
15084         }
15085
15086         if (this.isLocked()) {
15087             return;
15088         }
15089
15090         this.DDM.refreshCache(this.groups);
15091
15092         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15093         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15094         } else {
15095             if (this.clickValidator(e)) {
15096
15097                 // set the initial element position
15098                 this.setStartPosition();
15099
15100
15101                 this.b4MouseDown(e);
15102                 this.onMouseDown(e);
15103
15104                 this.DDM.handleMouseDown(e, this);
15105
15106                 this.DDM.stopEvent(e);
15107             } else {
15108
15109
15110             }
15111         }
15112     },
15113
15114     clickValidator: function(e) {
15115         var target = e.getTarget();
15116         return ( this.isValidHandleChild(target) &&
15117                     (this.id == this.handleElId ||
15118                         this.DDM.handleWasClicked(target, this.id)) );
15119     },
15120
15121     /**
15122      * Allows you to specify a tag name that should not start a drag operation
15123      * when clicked.  This is designed to facilitate embedding links within a
15124      * drag handle that do something other than start the drag.
15125      * @method addInvalidHandleType
15126      * @param {string} tagName the type of element to exclude
15127      */
15128     addInvalidHandleType: function(tagName) {
15129         var type = tagName.toUpperCase();
15130         this.invalidHandleTypes[type] = type;
15131     },
15132
15133     /**
15134      * Lets you to specify an element id for a child of a drag handle
15135      * that should not initiate a drag
15136      * @method addInvalidHandleId
15137      * @param {string} id the element id of the element you wish to ignore
15138      */
15139     addInvalidHandleId: function(id) {
15140         if (typeof id !== "string") {
15141             id = Roo.id(id);
15142         }
15143         this.invalidHandleIds[id] = id;
15144     },
15145
15146     /**
15147      * Lets you specify a css class of elements that will not initiate a drag
15148      * @method addInvalidHandleClass
15149      * @param {string} cssClass the class of the elements you wish to ignore
15150      */
15151     addInvalidHandleClass: function(cssClass) {
15152         this.invalidHandleClasses.push(cssClass);
15153     },
15154
15155     /**
15156      * Unsets an excluded tag name set by addInvalidHandleType
15157      * @method removeInvalidHandleType
15158      * @param {string} tagName the type of element to unexclude
15159      */
15160     removeInvalidHandleType: function(tagName) {
15161         var type = tagName.toUpperCase();
15162         // this.invalidHandleTypes[type] = null;
15163         delete this.invalidHandleTypes[type];
15164     },
15165
15166     /**
15167      * Unsets an invalid handle id
15168      * @method removeInvalidHandleId
15169      * @param {string} id the id of the element to re-enable
15170      */
15171     removeInvalidHandleId: function(id) {
15172         if (typeof id !== "string") {
15173             id = Roo.id(id);
15174         }
15175         delete this.invalidHandleIds[id];
15176     },
15177
15178     /**
15179      * Unsets an invalid css class
15180      * @method removeInvalidHandleClass
15181      * @param {string} cssClass the class of the element(s) you wish to
15182      * re-enable
15183      */
15184     removeInvalidHandleClass: function(cssClass) {
15185         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15186             if (this.invalidHandleClasses[i] == cssClass) {
15187                 delete this.invalidHandleClasses[i];
15188             }
15189         }
15190     },
15191
15192     /**
15193      * Checks the tag exclusion list to see if this click should be ignored
15194      * @method isValidHandleChild
15195      * @param {HTMLElement} node the HTMLElement to evaluate
15196      * @return {boolean} true if this is a valid tag type, false if not
15197      */
15198     isValidHandleChild: function(node) {
15199
15200         var valid = true;
15201         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15202         var nodeName;
15203         try {
15204             nodeName = node.nodeName.toUpperCase();
15205         } catch(e) {
15206             nodeName = node.nodeName;
15207         }
15208         valid = valid && !this.invalidHandleTypes[nodeName];
15209         valid = valid && !this.invalidHandleIds[node.id];
15210
15211         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15212             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15213         }
15214
15215
15216         return valid;
15217
15218     },
15219
15220     /**
15221      * Create the array of horizontal tick marks if an interval was specified
15222      * in setXConstraint().
15223      * @method setXTicks
15224      * @private
15225      */
15226     setXTicks: function(iStartX, iTickSize) {
15227         this.xTicks = [];
15228         this.xTickSize = iTickSize;
15229
15230         var tickMap = {};
15231
15232         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15233             if (!tickMap[i]) {
15234                 this.xTicks[this.xTicks.length] = i;
15235                 tickMap[i] = true;
15236             }
15237         }
15238
15239         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15240             if (!tickMap[i]) {
15241                 this.xTicks[this.xTicks.length] = i;
15242                 tickMap[i] = true;
15243             }
15244         }
15245
15246         this.xTicks.sort(this.DDM.numericSort) ;
15247     },
15248
15249     /**
15250      * Create the array of vertical tick marks if an interval was specified in
15251      * setYConstraint().
15252      * @method setYTicks
15253      * @private
15254      */
15255     setYTicks: function(iStartY, iTickSize) {
15256         this.yTicks = [];
15257         this.yTickSize = iTickSize;
15258
15259         var tickMap = {};
15260
15261         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15262             if (!tickMap[i]) {
15263                 this.yTicks[this.yTicks.length] = i;
15264                 tickMap[i] = true;
15265             }
15266         }
15267
15268         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15269             if (!tickMap[i]) {
15270                 this.yTicks[this.yTicks.length] = i;
15271                 tickMap[i] = true;
15272             }
15273         }
15274
15275         this.yTicks.sort(this.DDM.numericSort) ;
15276     },
15277
15278     /**
15279      * By default, the element can be dragged any place on the screen.  Use
15280      * this method to limit the horizontal travel of the element.  Pass in
15281      * 0,0 for the parameters if you want to lock the drag to the y axis.
15282      * @method setXConstraint
15283      * @param {int} iLeft the number of pixels the element can move to the left
15284      * @param {int} iRight the number of pixels the element can move to the
15285      * right
15286      * @param {int} iTickSize optional parameter for specifying that the
15287      * element
15288      * should move iTickSize pixels at a time.
15289      */
15290     setXConstraint: function(iLeft, iRight, iTickSize) {
15291         this.leftConstraint = iLeft;
15292         this.rightConstraint = iRight;
15293
15294         this.minX = this.initPageX - iLeft;
15295         this.maxX = this.initPageX + iRight;
15296         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15297
15298         this.constrainX = true;
15299     },
15300
15301     /**
15302      * Clears any constraints applied to this instance.  Also clears ticks
15303      * since they can't exist independent of a constraint at this time.
15304      * @method clearConstraints
15305      */
15306     clearConstraints: function() {
15307         this.constrainX = false;
15308         this.constrainY = false;
15309         this.clearTicks();
15310     },
15311
15312     /**
15313      * Clears any tick interval defined for this instance
15314      * @method clearTicks
15315      */
15316     clearTicks: function() {
15317         this.xTicks = null;
15318         this.yTicks = null;
15319         this.xTickSize = 0;
15320         this.yTickSize = 0;
15321     },
15322
15323     /**
15324      * By default, the element can be dragged any place on the screen.  Set
15325      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15326      * parameters if you want to lock the drag to the x axis.
15327      * @method setYConstraint
15328      * @param {int} iUp the number of pixels the element can move up
15329      * @param {int} iDown the number of pixels the element can move down
15330      * @param {int} iTickSize optional parameter for specifying that the
15331      * element should move iTickSize pixels at a time.
15332      */
15333     setYConstraint: function(iUp, iDown, iTickSize) {
15334         this.topConstraint = iUp;
15335         this.bottomConstraint = iDown;
15336
15337         this.minY = this.initPageY - iUp;
15338         this.maxY = this.initPageY + iDown;
15339         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15340
15341         this.constrainY = true;
15342
15343     },
15344
15345     /**
15346      * resetConstraints must be called if you manually reposition a dd element.
15347      * @method resetConstraints
15348      * @param {boolean} maintainOffset
15349      */
15350     resetConstraints: function() {
15351
15352
15353         // Maintain offsets if necessary
15354         if (this.initPageX || this.initPageX === 0) {
15355             // figure out how much this thing has moved
15356             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15357             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15358
15359             this.setInitPosition(dx, dy);
15360
15361         // This is the first time we have detected the element's position
15362         } else {
15363             this.setInitPosition();
15364         }
15365
15366         if (this.constrainX) {
15367             this.setXConstraint( this.leftConstraint,
15368                                  this.rightConstraint,
15369                                  this.xTickSize        );
15370         }
15371
15372         if (this.constrainY) {
15373             this.setYConstraint( this.topConstraint,
15374                                  this.bottomConstraint,
15375                                  this.yTickSize         );
15376         }
15377     },
15378
15379     /**
15380      * Normally the drag element is moved pixel by pixel, but we can specify
15381      * that it move a number of pixels at a time.  This method resolves the
15382      * location when we have it set up like this.
15383      * @method getTick
15384      * @param {int} val where we want to place the object
15385      * @param {int[]} tickArray sorted array of valid points
15386      * @return {int} the closest tick
15387      * @private
15388      */
15389     getTick: function(val, tickArray) {
15390
15391         if (!tickArray) {
15392             // If tick interval is not defined, it is effectively 1 pixel,
15393             // so we return the value passed to us.
15394             return val;
15395         } else if (tickArray[0] >= val) {
15396             // The value is lower than the first tick, so we return the first
15397             // tick.
15398             return tickArray[0];
15399         } else {
15400             for (var i=0, len=tickArray.length; i<len; ++i) {
15401                 var next = i + 1;
15402                 if (tickArray[next] && tickArray[next] >= val) {
15403                     var diff1 = val - tickArray[i];
15404                     var diff2 = tickArray[next] - val;
15405                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15406                 }
15407             }
15408
15409             // The value is larger than the last tick, so we return the last
15410             // tick.
15411             return tickArray[tickArray.length - 1];
15412         }
15413     },
15414
15415     /**
15416      * toString method
15417      * @method toString
15418      * @return {string} string representation of the dd obj
15419      */
15420     toString: function() {
15421         return ("DragDrop " + this.id);
15422     }
15423
15424 };
15425
15426 })();
15427 /*
15428  * Based on:
15429  * Ext JS Library 1.1.1
15430  * Copyright(c) 2006-2007, Ext JS, LLC.
15431  *
15432  * Originally Released Under LGPL - original licence link has changed is not relivant.
15433  *
15434  * Fork - LGPL
15435  * <script type="text/javascript">
15436  */
15437
15438
15439 /**
15440  * The drag and drop utility provides a framework for building drag and drop
15441  * applications.  In addition to enabling drag and drop for specific elements,
15442  * the drag and drop elements are tracked by the manager class, and the
15443  * interactions between the various elements are tracked during the drag and
15444  * the implementing code is notified about these important moments.
15445  */
15446
15447 // Only load the library once.  Rewriting the manager class would orphan
15448 // existing drag and drop instances.
15449 if (!Roo.dd.DragDropMgr) {
15450
15451 /**
15452  * @class Roo.dd.DragDropMgr
15453  * DragDropMgr is a singleton that tracks the element interaction for
15454  * all DragDrop items in the window.  Generally, you will not call
15455  * this class directly, but it does have helper methods that could
15456  * be useful in your DragDrop implementations.
15457  * @singleton
15458  */
15459 Roo.dd.DragDropMgr = function() {
15460
15461     var Event = Roo.EventManager;
15462
15463     return {
15464
15465         /**
15466          * Two dimensional Array of registered DragDrop objects.  The first
15467          * dimension is the DragDrop item group, the second the DragDrop
15468          * object.
15469          * @property ids
15470          * @type {string: string}
15471          * @private
15472          * @static
15473          */
15474         ids: {},
15475
15476         /**
15477          * Array of element ids defined as drag handles.  Used to determine
15478          * if the element that generated the mousedown event is actually the
15479          * handle and not the html element itself.
15480          * @property handleIds
15481          * @type {string: string}
15482          * @private
15483          * @static
15484          */
15485         handleIds: {},
15486
15487         /**
15488          * the DragDrop object that is currently being dragged
15489          * @property dragCurrent
15490          * @type DragDrop
15491          * @private
15492          * @static
15493          **/
15494         dragCurrent: null,
15495
15496         /**
15497          * the DragDrop object(s) that are being hovered over
15498          * @property dragOvers
15499          * @type Array
15500          * @private
15501          * @static
15502          */
15503         dragOvers: {},
15504
15505         /**
15506          * the X distance between the cursor and the object being dragged
15507          * @property deltaX
15508          * @type int
15509          * @private
15510          * @static
15511          */
15512         deltaX: 0,
15513
15514         /**
15515          * the Y distance between the cursor and the object being dragged
15516          * @property deltaY
15517          * @type int
15518          * @private
15519          * @static
15520          */
15521         deltaY: 0,
15522
15523         /**
15524          * Flag to determine if we should prevent the default behavior of the
15525          * events we define. By default this is true, but this can be set to
15526          * false if you need the default behavior (not recommended)
15527          * @property preventDefault
15528          * @type boolean
15529          * @static
15530          */
15531         preventDefault: true,
15532
15533         /**
15534          * Flag to determine if we should stop the propagation of the events
15535          * we generate. This is true by default but you may want to set it to
15536          * false if the html element contains other features that require the
15537          * mouse click.
15538          * @property stopPropagation
15539          * @type boolean
15540          * @static
15541          */
15542         stopPropagation: true,
15543
15544         /**
15545          * Internal flag that is set to true when drag and drop has been
15546          * intialized
15547          * @property initialized
15548          * @private
15549          * @static
15550          */
15551         initalized: false,
15552
15553         /**
15554          * All drag and drop can be disabled.
15555          * @property locked
15556          * @private
15557          * @static
15558          */
15559         locked: false,
15560
15561         /**
15562          * Called the first time an element is registered.
15563          * @method init
15564          * @private
15565          * @static
15566          */
15567         init: function() {
15568             this.initialized = true;
15569         },
15570
15571         /**
15572          * In point mode, drag and drop interaction is defined by the
15573          * location of the cursor during the drag/drop
15574          * @property POINT
15575          * @type int
15576          * @static
15577          */
15578         POINT: 0,
15579
15580         /**
15581          * In intersect mode, drag and drop interactio nis defined by the
15582          * overlap of two or more drag and drop objects.
15583          * @property INTERSECT
15584          * @type int
15585          * @static
15586          */
15587         INTERSECT: 1,
15588
15589         /**
15590          * The current drag and drop mode.  Default: POINT
15591          * @property mode
15592          * @type int
15593          * @static
15594          */
15595         mode: 0,
15596
15597         /**
15598          * Runs method on all drag and drop objects
15599          * @method _execOnAll
15600          * @private
15601          * @static
15602          */
15603         _execOnAll: function(sMethod, args) {
15604             for (var i in this.ids) {
15605                 for (var j in this.ids[i]) {
15606                     var oDD = this.ids[i][j];
15607                     if (! this.isTypeOfDD(oDD)) {
15608                         continue;
15609                     }
15610                     oDD[sMethod].apply(oDD, args);
15611                 }
15612             }
15613         },
15614
15615         /**
15616          * Drag and drop initialization.  Sets up the global event handlers
15617          * @method _onLoad
15618          * @private
15619          * @static
15620          */
15621         _onLoad: function() {
15622
15623             this.init();
15624
15625
15626             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15627             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15628             Event.on(window,   "unload",    this._onUnload, this, true);
15629             Event.on(window,   "resize",    this._onResize, this, true);
15630             // Event.on(window,   "mouseout",    this._test);
15631
15632         },
15633
15634         /**
15635          * Reset constraints on all drag and drop objs
15636          * @method _onResize
15637          * @private
15638          * @static
15639          */
15640         _onResize: function(e) {
15641             this._execOnAll("resetConstraints", []);
15642         },
15643
15644         /**
15645          * Lock all drag and drop functionality
15646          * @method lock
15647          * @static
15648          */
15649         lock: function() { this.locked = true; },
15650
15651         /**
15652          * Unlock all drag and drop functionality
15653          * @method unlock
15654          * @static
15655          */
15656         unlock: function() { this.locked = false; },
15657
15658         /**
15659          * Is drag and drop locked?
15660          * @method isLocked
15661          * @return {boolean} True if drag and drop is locked, false otherwise.
15662          * @static
15663          */
15664         isLocked: function() { return this.locked; },
15665
15666         /**
15667          * Location cache that is set for all drag drop objects when a drag is
15668          * initiated, cleared when the drag is finished.
15669          * @property locationCache
15670          * @private
15671          * @static
15672          */
15673         locationCache: {},
15674
15675         /**
15676          * Set useCache to false if you want to force object the lookup of each
15677          * drag and drop linked element constantly during a drag.
15678          * @property useCache
15679          * @type boolean
15680          * @static
15681          */
15682         useCache: true,
15683
15684         /**
15685          * The number of pixels that the mouse needs to move after the
15686          * mousedown before the drag is initiated.  Default=3;
15687          * @property clickPixelThresh
15688          * @type int
15689          * @static
15690          */
15691         clickPixelThresh: 3,
15692
15693         /**
15694          * The number of milliseconds after the mousedown event to initiate the
15695          * drag if we don't get a mouseup event. Default=1000
15696          * @property clickTimeThresh
15697          * @type int
15698          * @static
15699          */
15700         clickTimeThresh: 350,
15701
15702         /**
15703          * Flag that indicates that either the drag pixel threshold or the
15704          * mousdown time threshold has been met
15705          * @property dragThreshMet
15706          * @type boolean
15707          * @private
15708          * @static
15709          */
15710         dragThreshMet: false,
15711
15712         /**
15713          * Timeout used for the click time threshold
15714          * @property clickTimeout
15715          * @type Object
15716          * @private
15717          * @static
15718          */
15719         clickTimeout: null,
15720
15721         /**
15722          * The X position of the mousedown event stored for later use when a
15723          * drag threshold is met.
15724          * @property startX
15725          * @type int
15726          * @private
15727          * @static
15728          */
15729         startX: 0,
15730
15731         /**
15732          * The Y position of the mousedown event stored for later use when a
15733          * drag threshold is met.
15734          * @property startY
15735          * @type int
15736          * @private
15737          * @static
15738          */
15739         startY: 0,
15740
15741         /**
15742          * Each DragDrop instance must be registered with the DragDropMgr.
15743          * This is executed in DragDrop.init()
15744          * @method regDragDrop
15745          * @param {DragDrop} oDD the DragDrop object to register
15746          * @param {String} sGroup the name of the group this element belongs to
15747          * @static
15748          */
15749         regDragDrop: function(oDD, sGroup) {
15750             if (!this.initialized) { this.init(); }
15751
15752             if (!this.ids[sGroup]) {
15753                 this.ids[sGroup] = {};
15754             }
15755             this.ids[sGroup][oDD.id] = oDD;
15756         },
15757
15758         /**
15759          * Removes the supplied dd instance from the supplied group. Executed
15760          * by DragDrop.removeFromGroup, so don't call this function directly.
15761          * @method removeDDFromGroup
15762          * @private
15763          * @static
15764          */
15765         removeDDFromGroup: function(oDD, sGroup) {
15766             if (!this.ids[sGroup]) {
15767                 this.ids[sGroup] = {};
15768             }
15769
15770             var obj = this.ids[sGroup];
15771             if (obj && obj[oDD.id]) {
15772                 delete obj[oDD.id];
15773             }
15774         },
15775
15776         /**
15777          * Unregisters a drag and drop item.  This is executed in
15778          * DragDrop.unreg, use that method instead of calling this directly.
15779          * @method _remove
15780          * @private
15781          * @static
15782          */
15783         _remove: function(oDD) {
15784             for (var g in oDD.groups) {
15785                 if (g && this.ids[g][oDD.id]) {
15786                     delete this.ids[g][oDD.id];
15787                 }
15788             }
15789             delete this.handleIds[oDD.id];
15790         },
15791
15792         /**
15793          * Each DragDrop handle element must be registered.  This is done
15794          * automatically when executing DragDrop.setHandleElId()
15795          * @method regHandle
15796          * @param {String} sDDId the DragDrop id this element is a handle for
15797          * @param {String} sHandleId the id of the element that is the drag
15798          * handle
15799          * @static
15800          */
15801         regHandle: function(sDDId, sHandleId) {
15802             if (!this.handleIds[sDDId]) {
15803                 this.handleIds[sDDId] = {};
15804             }
15805             this.handleIds[sDDId][sHandleId] = sHandleId;
15806         },
15807
15808         /**
15809          * Utility function to determine if a given element has been
15810          * registered as a drag drop item.
15811          * @method isDragDrop
15812          * @param {String} id the element id to check
15813          * @return {boolean} true if this element is a DragDrop item,
15814          * false otherwise
15815          * @static
15816          */
15817         isDragDrop: function(id) {
15818             return ( this.getDDById(id) ) ? true : false;
15819         },
15820
15821         /**
15822          * Returns the drag and drop instances that are in all groups the
15823          * passed in instance belongs to.
15824          * @method getRelated
15825          * @param {DragDrop} p_oDD the obj to get related data for
15826          * @param {boolean} bTargetsOnly if true, only return targetable objs
15827          * @return {DragDrop[]} the related instances
15828          * @static
15829          */
15830         getRelated: function(p_oDD, bTargetsOnly) {
15831             var oDDs = [];
15832             for (var i in p_oDD.groups) {
15833                 for (j in this.ids[i]) {
15834                     var dd = this.ids[i][j];
15835                     if (! this.isTypeOfDD(dd)) {
15836                         continue;
15837                     }
15838                     if (!bTargetsOnly || dd.isTarget) {
15839                         oDDs[oDDs.length] = dd;
15840                     }
15841                 }
15842             }
15843
15844             return oDDs;
15845         },
15846
15847         /**
15848          * Returns true if the specified dd target is a legal target for
15849          * the specifice drag obj
15850          * @method isLegalTarget
15851          * @param {DragDrop} the drag obj
15852          * @param {DragDrop} the target
15853          * @return {boolean} true if the target is a legal target for the
15854          * dd obj
15855          * @static
15856          */
15857         isLegalTarget: function (oDD, oTargetDD) {
15858             var targets = this.getRelated(oDD, true);
15859             for (var i=0, len=targets.length;i<len;++i) {
15860                 if (targets[i].id == oTargetDD.id) {
15861                     return true;
15862                 }
15863             }
15864
15865             return false;
15866         },
15867
15868         /**
15869          * My goal is to be able to transparently determine if an object is
15870          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15871          * returns "object", oDD.constructor.toString() always returns
15872          * "DragDrop" and not the name of the subclass.  So for now it just
15873          * evaluates a well-known variable in DragDrop.
15874          * @method isTypeOfDD
15875          * @param {Object} the object to evaluate
15876          * @return {boolean} true if typeof oDD = DragDrop
15877          * @static
15878          */
15879         isTypeOfDD: function (oDD) {
15880             return (oDD && oDD.__ygDragDrop);
15881         },
15882
15883         /**
15884          * Utility function to determine if a given element has been
15885          * registered as a drag drop handle for the given Drag Drop object.
15886          * @method isHandle
15887          * @param {String} id the element id to check
15888          * @return {boolean} true if this element is a DragDrop handle, false
15889          * otherwise
15890          * @static
15891          */
15892         isHandle: function(sDDId, sHandleId) {
15893             return ( this.handleIds[sDDId] &&
15894                             this.handleIds[sDDId][sHandleId] );
15895         },
15896
15897         /**
15898          * Returns the DragDrop instance for a given id
15899          * @method getDDById
15900          * @param {String} id the id of the DragDrop object
15901          * @return {DragDrop} the drag drop object, null if it is not found
15902          * @static
15903          */
15904         getDDById: function(id) {
15905             for (var i in this.ids) {
15906                 if (this.ids[i][id]) {
15907                     return this.ids[i][id];
15908                 }
15909             }
15910             return null;
15911         },
15912
15913         /**
15914          * Fired after a registered DragDrop object gets the mousedown event.
15915          * Sets up the events required to track the object being dragged
15916          * @method handleMouseDown
15917          * @param {Event} e the event
15918          * @param oDD the DragDrop object being dragged
15919          * @private
15920          * @static
15921          */
15922         handleMouseDown: function(e, oDD) {
15923             if(Roo.QuickTips){
15924                 Roo.QuickTips.disable();
15925             }
15926             this.currentTarget = e.getTarget();
15927
15928             this.dragCurrent = oDD;
15929
15930             var el = oDD.getEl();
15931
15932             // track start position
15933             this.startX = e.getPageX();
15934             this.startY = e.getPageY();
15935
15936             this.deltaX = this.startX - el.offsetLeft;
15937             this.deltaY = this.startY - el.offsetTop;
15938
15939             this.dragThreshMet = false;
15940
15941             this.clickTimeout = setTimeout(
15942                     function() {
15943                         var DDM = Roo.dd.DDM;
15944                         DDM.startDrag(DDM.startX, DDM.startY);
15945                     },
15946                     this.clickTimeThresh );
15947         },
15948
15949         /**
15950          * Fired when either the drag pixel threshol or the mousedown hold
15951          * time threshold has been met.
15952          * @method startDrag
15953          * @param x {int} the X position of the original mousedown
15954          * @param y {int} the Y position of the original mousedown
15955          * @static
15956          */
15957         startDrag: function(x, y) {
15958             clearTimeout(this.clickTimeout);
15959             if (this.dragCurrent) {
15960                 this.dragCurrent.b4StartDrag(x, y);
15961                 this.dragCurrent.startDrag(x, y);
15962             }
15963             this.dragThreshMet = true;
15964         },
15965
15966         /**
15967          * Internal function to handle the mouseup event.  Will be invoked
15968          * from the context of the document.
15969          * @method handleMouseUp
15970          * @param {Event} e the event
15971          * @private
15972          * @static
15973          */
15974         handleMouseUp: function(e) {
15975
15976             if(Roo.QuickTips){
15977                 Roo.QuickTips.enable();
15978             }
15979             if (! this.dragCurrent) {
15980                 return;
15981             }
15982
15983             clearTimeout(this.clickTimeout);
15984
15985             if (this.dragThreshMet) {
15986                 this.fireEvents(e, true);
15987             } else {
15988             }
15989
15990             this.stopDrag(e);
15991
15992             this.stopEvent(e);
15993         },
15994
15995         /**
15996          * Utility to stop event propagation and event default, if these
15997          * features are turned on.
15998          * @method stopEvent
15999          * @param {Event} e the event as returned by this.getEvent()
16000          * @static
16001          */
16002         stopEvent: function(e){
16003             if(this.stopPropagation) {
16004                 e.stopPropagation();
16005             }
16006
16007             if (this.preventDefault) {
16008                 e.preventDefault();
16009             }
16010         },
16011
16012         /**
16013          * Internal function to clean up event handlers after the drag
16014          * operation is complete
16015          * @method stopDrag
16016          * @param {Event} e the event
16017          * @private
16018          * @static
16019          */
16020         stopDrag: function(e) {
16021             // Fire the drag end event for the item that was dragged
16022             if (this.dragCurrent) {
16023                 if (this.dragThreshMet) {
16024                     this.dragCurrent.b4EndDrag(e);
16025                     this.dragCurrent.endDrag(e);
16026                 }
16027
16028                 this.dragCurrent.onMouseUp(e);
16029             }
16030
16031             this.dragCurrent = null;
16032             this.dragOvers = {};
16033         },
16034
16035         /**
16036          * Internal function to handle the mousemove event.  Will be invoked
16037          * from the context of the html element.
16038          *
16039          * @TODO figure out what we can do about mouse events lost when the
16040          * user drags objects beyond the window boundary.  Currently we can
16041          * detect this in internet explorer by verifying that the mouse is
16042          * down during the mousemove event.  Firefox doesn't give us the
16043          * button state on the mousemove event.
16044          * @method handleMouseMove
16045          * @param {Event} e the event
16046          * @private
16047          * @static
16048          */
16049         handleMouseMove: function(e) {
16050             if (! this.dragCurrent) {
16051                 return true;
16052             }
16053
16054             // var button = e.which || e.button;
16055
16056             // check for IE mouseup outside of page boundary
16057             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16058                 this.stopEvent(e);
16059                 return this.handleMouseUp(e);
16060             }
16061
16062             if (!this.dragThreshMet) {
16063                 var diffX = Math.abs(this.startX - e.getPageX());
16064                 var diffY = Math.abs(this.startY - e.getPageY());
16065                 if (diffX > this.clickPixelThresh ||
16066                             diffY > this.clickPixelThresh) {
16067                     this.startDrag(this.startX, this.startY);
16068                 }
16069             }
16070
16071             if (this.dragThreshMet) {
16072                 this.dragCurrent.b4Drag(e);
16073                 this.dragCurrent.onDrag(e);
16074                 if(!this.dragCurrent.moveOnly){
16075                     this.fireEvents(e, false);
16076                 }
16077             }
16078
16079             this.stopEvent(e);
16080
16081             return true;
16082         },
16083
16084         /**
16085          * Iterates over all of the DragDrop elements to find ones we are
16086          * hovering over or dropping on
16087          * @method fireEvents
16088          * @param {Event} e the event
16089          * @param {boolean} isDrop is this a drop op or a mouseover op?
16090          * @private
16091          * @static
16092          */
16093         fireEvents: function(e, isDrop) {
16094             var dc = this.dragCurrent;
16095
16096             // If the user did the mouse up outside of the window, we could
16097             // get here even though we have ended the drag.
16098             if (!dc || dc.isLocked()) {
16099                 return;
16100             }
16101
16102             var pt = e.getPoint();
16103
16104             // cache the previous dragOver array
16105             var oldOvers = [];
16106
16107             var outEvts   = [];
16108             var overEvts  = [];
16109             var dropEvts  = [];
16110             var enterEvts = [];
16111
16112             // Check to see if the object(s) we were hovering over is no longer
16113             // being hovered over so we can fire the onDragOut event
16114             for (var i in this.dragOvers) {
16115
16116                 var ddo = this.dragOvers[i];
16117
16118                 if (! this.isTypeOfDD(ddo)) {
16119                     continue;
16120                 }
16121
16122                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16123                     outEvts.push( ddo );
16124                 }
16125
16126                 oldOvers[i] = true;
16127                 delete this.dragOvers[i];
16128             }
16129
16130             for (var sGroup in dc.groups) {
16131
16132                 if ("string" != typeof sGroup) {
16133                     continue;
16134                 }
16135
16136                 for (i in this.ids[sGroup]) {
16137                     var oDD = this.ids[sGroup][i];
16138                     if (! this.isTypeOfDD(oDD)) {
16139                         continue;
16140                     }
16141
16142                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16143                         if (this.isOverTarget(pt, oDD, this.mode)) {
16144                             // look for drop interactions
16145                             if (isDrop) {
16146                                 dropEvts.push( oDD );
16147                             // look for drag enter and drag over interactions
16148                             } else {
16149
16150                                 // initial drag over: dragEnter fires
16151                                 if (!oldOvers[oDD.id]) {
16152                                     enterEvts.push( oDD );
16153                                 // subsequent drag overs: dragOver fires
16154                                 } else {
16155                                     overEvts.push( oDD );
16156                                 }
16157
16158                                 this.dragOvers[oDD.id] = oDD;
16159                             }
16160                         }
16161                     }
16162                 }
16163             }
16164
16165             if (this.mode) {
16166                 if (outEvts.length) {
16167                     dc.b4DragOut(e, outEvts);
16168                     dc.onDragOut(e, outEvts);
16169                 }
16170
16171                 if (enterEvts.length) {
16172                     dc.onDragEnter(e, enterEvts);
16173                 }
16174
16175                 if (overEvts.length) {
16176                     dc.b4DragOver(e, overEvts);
16177                     dc.onDragOver(e, overEvts);
16178                 }
16179
16180                 if (dropEvts.length) {
16181                     dc.b4DragDrop(e, dropEvts);
16182                     dc.onDragDrop(e, dropEvts);
16183                 }
16184
16185             } else {
16186                 // fire dragout events
16187                 var len = 0;
16188                 for (i=0, len=outEvts.length; i<len; ++i) {
16189                     dc.b4DragOut(e, outEvts[i].id);
16190                     dc.onDragOut(e, outEvts[i].id);
16191                 }
16192
16193                 // fire enter events
16194                 for (i=0,len=enterEvts.length; i<len; ++i) {
16195                     // dc.b4DragEnter(e, oDD.id);
16196                     dc.onDragEnter(e, enterEvts[i].id);
16197                 }
16198
16199                 // fire over events
16200                 for (i=0,len=overEvts.length; i<len; ++i) {
16201                     dc.b4DragOver(e, overEvts[i].id);
16202                     dc.onDragOver(e, overEvts[i].id);
16203                 }
16204
16205                 // fire drop events
16206                 for (i=0, len=dropEvts.length; i<len; ++i) {
16207                     dc.b4DragDrop(e, dropEvts[i].id);
16208                     dc.onDragDrop(e, dropEvts[i].id);
16209                 }
16210
16211             }
16212
16213             // notify about a drop that did not find a target
16214             if (isDrop && !dropEvts.length) {
16215                 dc.onInvalidDrop(e);
16216             }
16217
16218         },
16219
16220         /**
16221          * Helper function for getting the best match from the list of drag
16222          * and drop objects returned by the drag and drop events when we are
16223          * in INTERSECT mode.  It returns either the first object that the
16224          * cursor is over, or the object that has the greatest overlap with
16225          * the dragged element.
16226          * @method getBestMatch
16227          * @param  {DragDrop[]} dds The array of drag and drop objects
16228          * targeted
16229          * @return {DragDrop}       The best single match
16230          * @static
16231          */
16232         getBestMatch: function(dds) {
16233             var winner = null;
16234             // Return null if the input is not what we expect
16235             //if (!dds || !dds.length || dds.length == 0) {
16236                // winner = null;
16237             // If there is only one item, it wins
16238             //} else if (dds.length == 1) {
16239
16240             var len = dds.length;
16241
16242             if (len == 1) {
16243                 winner = dds[0];
16244             } else {
16245                 // Loop through the targeted items
16246                 for (var i=0; i<len; ++i) {
16247                     var dd = dds[i];
16248                     // If the cursor is over the object, it wins.  If the
16249                     // cursor is over multiple matches, the first one we come
16250                     // to wins.
16251                     if (dd.cursorIsOver) {
16252                         winner = dd;
16253                         break;
16254                     // Otherwise the object with the most overlap wins
16255                     } else {
16256                         if (!winner ||
16257                             winner.overlap.getArea() < dd.overlap.getArea()) {
16258                             winner = dd;
16259                         }
16260                     }
16261                 }
16262             }
16263
16264             return winner;
16265         },
16266
16267         /**
16268          * Refreshes the cache of the top-left and bottom-right points of the
16269          * drag and drop objects in the specified group(s).  This is in the
16270          * format that is stored in the drag and drop instance, so typical
16271          * usage is:
16272          * <code>
16273          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16274          * </code>
16275          * Alternatively:
16276          * <code>
16277          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16278          * </code>
16279          * @TODO this really should be an indexed array.  Alternatively this
16280          * method could accept both.
16281          * @method refreshCache
16282          * @param {Object} groups an associative array of groups to refresh
16283          * @static
16284          */
16285         refreshCache: function(groups) {
16286             for (var sGroup in groups) {
16287                 if ("string" != typeof sGroup) {
16288                     continue;
16289                 }
16290                 for (var i in this.ids[sGroup]) {
16291                     var oDD = this.ids[sGroup][i];
16292
16293                     if (this.isTypeOfDD(oDD)) {
16294                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16295                         var loc = this.getLocation(oDD);
16296                         if (loc) {
16297                             this.locationCache[oDD.id] = loc;
16298                         } else {
16299                             delete this.locationCache[oDD.id];
16300                             // this will unregister the drag and drop object if
16301                             // the element is not in a usable state
16302                             // oDD.unreg();
16303                         }
16304                     }
16305                 }
16306             }
16307         },
16308
16309         /**
16310          * This checks to make sure an element exists and is in the DOM.  The
16311          * main purpose is to handle cases where innerHTML is used to remove
16312          * drag and drop objects from the DOM.  IE provides an 'unspecified
16313          * error' when trying to access the offsetParent of such an element
16314          * @method verifyEl
16315          * @param {HTMLElement} el the element to check
16316          * @return {boolean} true if the element looks usable
16317          * @static
16318          */
16319         verifyEl: function(el) {
16320             if (el) {
16321                 var parent;
16322                 if(Roo.isIE){
16323                     try{
16324                         parent = el.offsetParent;
16325                     }catch(e){}
16326                 }else{
16327                     parent = el.offsetParent;
16328                 }
16329                 if (parent) {
16330                     return true;
16331                 }
16332             }
16333
16334             return false;
16335         },
16336
16337         /**
16338          * Returns a Region object containing the drag and drop element's position
16339          * and size, including the padding configured for it
16340          * @method getLocation
16341          * @param {DragDrop} oDD the drag and drop object to get the
16342          *                       location for
16343          * @return {Roo.lib.Region} a Region object representing the total area
16344          *                             the element occupies, including any padding
16345          *                             the instance is configured for.
16346          * @static
16347          */
16348         getLocation: function(oDD) {
16349             if (! this.isTypeOfDD(oDD)) {
16350                 return null;
16351             }
16352
16353             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16354
16355             try {
16356                 pos= Roo.lib.Dom.getXY(el);
16357             } catch (e) { }
16358
16359             if (!pos) {
16360                 return null;
16361             }
16362
16363             x1 = pos[0];
16364             x2 = x1 + el.offsetWidth;
16365             y1 = pos[1];
16366             y2 = y1 + el.offsetHeight;
16367
16368             t = y1 - oDD.padding[0];
16369             r = x2 + oDD.padding[1];
16370             b = y2 + oDD.padding[2];
16371             l = x1 - oDD.padding[3];
16372
16373             return new Roo.lib.Region( t, r, b, l );
16374         },
16375
16376         /**
16377          * Checks the cursor location to see if it over the target
16378          * @method isOverTarget
16379          * @param {Roo.lib.Point} pt The point to evaluate
16380          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16381          * @return {boolean} true if the mouse is over the target
16382          * @private
16383          * @static
16384          */
16385         isOverTarget: function(pt, oTarget, intersect) {
16386             // use cache if available
16387             var loc = this.locationCache[oTarget.id];
16388             if (!loc || !this.useCache) {
16389                 loc = this.getLocation(oTarget);
16390                 this.locationCache[oTarget.id] = loc;
16391
16392             }
16393
16394             if (!loc) {
16395                 return false;
16396             }
16397
16398             oTarget.cursorIsOver = loc.contains( pt );
16399
16400             // DragDrop is using this as a sanity check for the initial mousedown
16401             // in this case we are done.  In POINT mode, if the drag obj has no
16402             // contraints, we are also done. Otherwise we need to evaluate the
16403             // location of the target as related to the actual location of the
16404             // dragged element.
16405             var dc = this.dragCurrent;
16406             if (!dc || !dc.getTargetCoord ||
16407                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16408                 return oTarget.cursorIsOver;
16409             }
16410
16411             oTarget.overlap = null;
16412
16413             // Get the current location of the drag element, this is the
16414             // location of the mouse event less the delta that represents
16415             // where the original mousedown happened on the element.  We
16416             // need to consider constraints and ticks as well.
16417             var pos = dc.getTargetCoord(pt.x, pt.y);
16418
16419             var el = dc.getDragEl();
16420             var curRegion = new Roo.lib.Region( pos.y,
16421                                                    pos.x + el.offsetWidth,
16422                                                    pos.y + el.offsetHeight,
16423                                                    pos.x );
16424
16425             var overlap = curRegion.intersect(loc);
16426
16427             if (overlap) {
16428                 oTarget.overlap = overlap;
16429                 return (intersect) ? true : oTarget.cursorIsOver;
16430             } else {
16431                 return false;
16432             }
16433         },
16434
16435         /**
16436          * unload event handler
16437          * @method _onUnload
16438          * @private
16439          * @static
16440          */
16441         _onUnload: function(e, me) {
16442             Roo.dd.DragDropMgr.unregAll();
16443         },
16444
16445         /**
16446          * Cleans up the drag and drop events and objects.
16447          * @method unregAll
16448          * @private
16449          * @static
16450          */
16451         unregAll: function() {
16452
16453             if (this.dragCurrent) {
16454                 this.stopDrag();
16455                 this.dragCurrent = null;
16456             }
16457
16458             this._execOnAll("unreg", []);
16459
16460             for (i in this.elementCache) {
16461                 delete this.elementCache[i];
16462             }
16463
16464             this.elementCache = {};
16465             this.ids = {};
16466         },
16467
16468         /**
16469          * A cache of DOM elements
16470          * @property elementCache
16471          * @private
16472          * @static
16473          */
16474         elementCache: {},
16475
16476         /**
16477          * Get the wrapper for the DOM element specified
16478          * @method getElWrapper
16479          * @param {String} id the id of the element to get
16480          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16481          * @private
16482          * @deprecated This wrapper isn't that useful
16483          * @static
16484          */
16485         getElWrapper: function(id) {
16486             var oWrapper = this.elementCache[id];
16487             if (!oWrapper || !oWrapper.el) {
16488                 oWrapper = this.elementCache[id] =
16489                     new this.ElementWrapper(Roo.getDom(id));
16490             }
16491             return oWrapper;
16492         },
16493
16494         /**
16495          * Returns the actual DOM element
16496          * @method getElement
16497          * @param {String} id the id of the elment to get
16498          * @return {Object} The element
16499          * @deprecated use Roo.getDom instead
16500          * @static
16501          */
16502         getElement: function(id) {
16503             return Roo.getDom(id);
16504         },
16505
16506         /**
16507          * Returns the style property for the DOM element (i.e.,
16508          * document.getElById(id).style)
16509          * @method getCss
16510          * @param {String} id the id of the elment to get
16511          * @return {Object} The style property of the element
16512          * @deprecated use Roo.getDom instead
16513          * @static
16514          */
16515         getCss: function(id) {
16516             var el = Roo.getDom(id);
16517             return (el) ? el.style : null;
16518         },
16519
16520         /**
16521          * Inner class for cached elements
16522          * @class DragDropMgr.ElementWrapper
16523          * @for DragDropMgr
16524          * @private
16525          * @deprecated
16526          */
16527         ElementWrapper: function(el) {
16528                 /**
16529                  * The element
16530                  * @property el
16531                  */
16532                 this.el = el || null;
16533                 /**
16534                  * The element id
16535                  * @property id
16536                  */
16537                 this.id = this.el && el.id;
16538                 /**
16539                  * A reference to the style property
16540                  * @property css
16541                  */
16542                 this.css = this.el && el.style;
16543             },
16544
16545         /**
16546          * Returns the X position of an html element
16547          * @method getPosX
16548          * @param el the element for which to get the position
16549          * @return {int} the X coordinate
16550          * @for DragDropMgr
16551          * @deprecated use Roo.lib.Dom.getX instead
16552          * @static
16553          */
16554         getPosX: function(el) {
16555             return Roo.lib.Dom.getX(el);
16556         },
16557
16558         /**
16559          * Returns the Y position of an html element
16560          * @method getPosY
16561          * @param el the element for which to get the position
16562          * @return {int} the Y coordinate
16563          * @deprecated use Roo.lib.Dom.getY instead
16564          * @static
16565          */
16566         getPosY: function(el) {
16567             return Roo.lib.Dom.getY(el);
16568         },
16569
16570         /**
16571          * Swap two nodes.  In IE, we use the native method, for others we
16572          * emulate the IE behavior
16573          * @method swapNode
16574          * @param n1 the first node to swap
16575          * @param n2 the other node to swap
16576          * @static
16577          */
16578         swapNode: function(n1, n2) {
16579             if (n1.swapNode) {
16580                 n1.swapNode(n2);
16581             } else {
16582                 var p = n2.parentNode;
16583                 var s = n2.nextSibling;
16584
16585                 if (s == n1) {
16586                     p.insertBefore(n1, n2);
16587                 } else if (n2 == n1.nextSibling) {
16588                     p.insertBefore(n2, n1);
16589                 } else {
16590                     n1.parentNode.replaceChild(n2, n1);
16591                     p.insertBefore(n1, s);
16592                 }
16593             }
16594         },
16595
16596         /**
16597          * Returns the current scroll position
16598          * @method getScroll
16599          * @private
16600          * @static
16601          */
16602         getScroll: function () {
16603             var t, l, dde=document.documentElement, db=document.body;
16604             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16605                 t = dde.scrollTop;
16606                 l = dde.scrollLeft;
16607             } else if (db) {
16608                 t = db.scrollTop;
16609                 l = db.scrollLeft;
16610             } else {
16611
16612             }
16613             return { top: t, left: l };
16614         },
16615
16616         /**
16617          * Returns the specified element style property
16618          * @method getStyle
16619          * @param {HTMLElement} el          the element
16620          * @param {string}      styleProp   the style property
16621          * @return {string} The value of the style property
16622          * @deprecated use Roo.lib.Dom.getStyle
16623          * @static
16624          */
16625         getStyle: function(el, styleProp) {
16626             return Roo.fly(el).getStyle(styleProp);
16627         },
16628
16629         /**
16630          * Gets the scrollTop
16631          * @method getScrollTop
16632          * @return {int} the document's scrollTop
16633          * @static
16634          */
16635         getScrollTop: function () { return this.getScroll().top; },
16636
16637         /**
16638          * Gets the scrollLeft
16639          * @method getScrollLeft
16640          * @return {int} the document's scrollTop
16641          * @static
16642          */
16643         getScrollLeft: function () { return this.getScroll().left; },
16644
16645         /**
16646          * Sets the x/y position of an element to the location of the
16647          * target element.
16648          * @method moveToEl
16649          * @param {HTMLElement} moveEl      The element to move
16650          * @param {HTMLElement} targetEl    The position reference element
16651          * @static
16652          */
16653         moveToEl: function (moveEl, targetEl) {
16654             var aCoord = Roo.lib.Dom.getXY(targetEl);
16655             Roo.lib.Dom.setXY(moveEl, aCoord);
16656         },
16657
16658         /**
16659          * Numeric array sort function
16660          * @method numericSort
16661          * @static
16662          */
16663         numericSort: function(a, b) { return (a - b); },
16664
16665         /**
16666          * Internal counter
16667          * @property _timeoutCount
16668          * @private
16669          * @static
16670          */
16671         _timeoutCount: 0,
16672
16673         /**
16674          * Trying to make the load order less important.  Without this we get
16675          * an error if this file is loaded before the Event Utility.
16676          * @method _addListeners
16677          * @private
16678          * @static
16679          */
16680         _addListeners: function() {
16681             var DDM = Roo.dd.DDM;
16682             if ( Roo.lib.Event && document ) {
16683                 DDM._onLoad();
16684             } else {
16685                 if (DDM._timeoutCount > 2000) {
16686                 } else {
16687                     setTimeout(DDM._addListeners, 10);
16688                     if (document && document.body) {
16689                         DDM._timeoutCount += 1;
16690                     }
16691                 }
16692             }
16693         },
16694
16695         /**
16696          * Recursively searches the immediate parent and all child nodes for
16697          * the handle element in order to determine wheter or not it was
16698          * clicked.
16699          * @method handleWasClicked
16700          * @param node the html element to inspect
16701          * @static
16702          */
16703         handleWasClicked: function(node, id) {
16704             if (this.isHandle(id, node.id)) {
16705                 return true;
16706             } else {
16707                 // check to see if this is a text node child of the one we want
16708                 var p = node.parentNode;
16709
16710                 while (p) {
16711                     if (this.isHandle(id, p.id)) {
16712                         return true;
16713                     } else {
16714                         p = p.parentNode;
16715                     }
16716                 }
16717             }
16718
16719             return false;
16720         }
16721
16722     };
16723
16724 }();
16725
16726 // shorter alias, save a few bytes
16727 Roo.dd.DDM = Roo.dd.DragDropMgr;
16728 Roo.dd.DDM._addListeners();
16729
16730 }/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.dd.DD
16743  * A DragDrop implementation where the linked element follows the
16744  * mouse cursor during a drag.
16745  * @extends Roo.dd.DragDrop
16746  * @constructor
16747  * @param {String} id the id of the linked element
16748  * @param {String} sGroup the group of related DragDrop items
16749  * @param {object} config an object containing configurable attributes
16750  *                Valid properties for DD:
16751  *                    scroll
16752  */
16753 Roo.dd.DD = function(id, sGroup, config) {
16754     if (id) {
16755         this.init(id, sGroup, config);
16756     }
16757 };
16758
16759 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16760
16761     /**
16762      * When set to true, the utility automatically tries to scroll the browser
16763      * window wehn a drag and drop element is dragged near the viewport boundary.
16764      * Defaults to true.
16765      * @property scroll
16766      * @type boolean
16767      */
16768     scroll: true,
16769
16770     /**
16771      * Sets the pointer offset to the distance between the linked element's top
16772      * left corner and the location the element was clicked
16773      * @method autoOffset
16774      * @param {int} iPageX the X coordinate of the click
16775      * @param {int} iPageY the Y coordinate of the click
16776      */
16777     autoOffset: function(iPageX, iPageY) {
16778         var x = iPageX - this.startPageX;
16779         var y = iPageY - this.startPageY;
16780         this.setDelta(x, y);
16781     },
16782
16783     /**
16784      * Sets the pointer offset.  You can call this directly to force the
16785      * offset to be in a particular location (e.g., pass in 0,0 to set it
16786      * to the center of the object)
16787      * @method setDelta
16788      * @param {int} iDeltaX the distance from the left
16789      * @param {int} iDeltaY the distance from the top
16790      */
16791     setDelta: function(iDeltaX, iDeltaY) {
16792         this.deltaX = iDeltaX;
16793         this.deltaY = iDeltaY;
16794     },
16795
16796     /**
16797      * Sets the drag element to the location of the mousedown or click event,
16798      * maintaining the cursor location relative to the location on the element
16799      * that was clicked.  Override this if you want to place the element in a
16800      * location other than where the cursor is.
16801      * @method setDragElPos
16802      * @param {int} iPageX the X coordinate of the mousedown or drag event
16803      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16804      */
16805     setDragElPos: function(iPageX, iPageY) {
16806         // the first time we do this, we are going to check to make sure
16807         // the element has css positioning
16808
16809         var el = this.getDragEl();
16810         this.alignElWithMouse(el, iPageX, iPageY);
16811     },
16812
16813     /**
16814      * Sets the element to the location of the mousedown or click event,
16815      * maintaining the cursor location relative to the location on the element
16816      * that was clicked.  Override this if you want to place the element in a
16817      * location other than where the cursor is.
16818      * @method alignElWithMouse
16819      * @param {HTMLElement} el the element to move
16820      * @param {int} iPageX the X coordinate of the mousedown or drag event
16821      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16822      */
16823     alignElWithMouse: function(el, iPageX, iPageY) {
16824         var oCoord = this.getTargetCoord(iPageX, iPageY);
16825         var fly = el.dom ? el : Roo.fly(el);
16826         if (!this.deltaSetXY) {
16827             var aCoord = [oCoord.x, oCoord.y];
16828             fly.setXY(aCoord);
16829             var newLeft = fly.getLeft(true);
16830             var newTop  = fly.getTop(true);
16831             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16832         } else {
16833             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16834         }
16835
16836         this.cachePosition(oCoord.x, oCoord.y);
16837         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16838         return oCoord;
16839     },
16840
16841     /**
16842      * Saves the most recent position so that we can reset the constraints and
16843      * tick marks on-demand.  We need to know this so that we can calculate the
16844      * number of pixels the element is offset from its original position.
16845      * @method cachePosition
16846      * @param iPageX the current x position (optional, this just makes it so we
16847      * don't have to look it up again)
16848      * @param iPageY the current y position (optional, this just makes it so we
16849      * don't have to look it up again)
16850      */
16851     cachePosition: function(iPageX, iPageY) {
16852         if (iPageX) {
16853             this.lastPageX = iPageX;
16854             this.lastPageY = iPageY;
16855         } else {
16856             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16857             this.lastPageX = aCoord[0];
16858             this.lastPageY = aCoord[1];
16859         }
16860     },
16861
16862     /**
16863      * Auto-scroll the window if the dragged object has been moved beyond the
16864      * visible window boundary.
16865      * @method autoScroll
16866      * @param {int} x the drag element's x position
16867      * @param {int} y the drag element's y position
16868      * @param {int} h the height of the drag element
16869      * @param {int} w the width of the drag element
16870      * @private
16871      */
16872     autoScroll: function(x, y, h, w) {
16873
16874         if (this.scroll) {
16875             // The client height
16876             var clientH = Roo.lib.Dom.getViewWidth();
16877
16878             // The client width
16879             var clientW = Roo.lib.Dom.getViewHeight();
16880
16881             // The amt scrolled down
16882             var st = this.DDM.getScrollTop();
16883
16884             // The amt scrolled right
16885             var sl = this.DDM.getScrollLeft();
16886
16887             // Location of the bottom of the element
16888             var bot = h + y;
16889
16890             // Location of the right of the element
16891             var right = w + x;
16892
16893             // The distance from the cursor to the bottom of the visible area,
16894             // adjusted so that we don't scroll if the cursor is beyond the
16895             // element drag constraints
16896             var toBot = (clientH + st - y - this.deltaY);
16897
16898             // The distance from the cursor to the right of the visible area
16899             var toRight = (clientW + sl - x - this.deltaX);
16900
16901
16902             // How close to the edge the cursor must be before we scroll
16903             // var thresh = (document.all) ? 100 : 40;
16904             var thresh = 40;
16905
16906             // How many pixels to scroll per autoscroll op.  This helps to reduce
16907             // clunky scrolling. IE is more sensitive about this ... it needs this
16908             // value to be higher.
16909             var scrAmt = (document.all) ? 80 : 30;
16910
16911             // Scroll down if we are near the bottom of the visible page and the
16912             // obj extends below the crease
16913             if ( bot > clientH && toBot < thresh ) {
16914                 window.scrollTo(sl, st + scrAmt);
16915             }
16916
16917             // Scroll up if the window is scrolled down and the top of the object
16918             // goes above the top border
16919             if ( y < st && st > 0 && y - st < thresh ) {
16920                 window.scrollTo(sl, st - scrAmt);
16921             }
16922
16923             // Scroll right if the obj is beyond the right border and the cursor is
16924             // near the border.
16925             if ( right > clientW && toRight < thresh ) {
16926                 window.scrollTo(sl + scrAmt, st);
16927             }
16928
16929             // Scroll left if the window has been scrolled to the right and the obj
16930             // extends past the left border
16931             if ( x < sl && sl > 0 && x - sl < thresh ) {
16932                 window.scrollTo(sl - scrAmt, st);
16933             }
16934         }
16935     },
16936
16937     /**
16938      * Finds the location the element should be placed if we want to move
16939      * it to where the mouse location less the click offset would place us.
16940      * @method getTargetCoord
16941      * @param {int} iPageX the X coordinate of the click
16942      * @param {int} iPageY the Y coordinate of the click
16943      * @return an object that contains the coordinates (Object.x and Object.y)
16944      * @private
16945      */
16946     getTargetCoord: function(iPageX, iPageY) {
16947
16948
16949         var x = iPageX - this.deltaX;
16950         var y = iPageY - this.deltaY;
16951
16952         if (this.constrainX) {
16953             if (x < this.minX) { x = this.minX; }
16954             if (x > this.maxX) { x = this.maxX; }
16955         }
16956
16957         if (this.constrainY) {
16958             if (y < this.minY) { y = this.minY; }
16959             if (y > this.maxY) { y = this.maxY; }
16960         }
16961
16962         x = this.getTick(x, this.xTicks);
16963         y = this.getTick(y, this.yTicks);
16964
16965
16966         return {x:x, y:y};
16967     },
16968
16969     /*
16970      * Sets up config options specific to this class. Overrides
16971      * Roo.dd.DragDrop, but all versions of this method through the
16972      * inheritance chain are called
16973      */
16974     applyConfig: function() {
16975         Roo.dd.DD.superclass.applyConfig.call(this);
16976         this.scroll = (this.config.scroll !== false);
16977     },
16978
16979     /*
16980      * Event that fires prior to the onMouseDown event.  Overrides
16981      * Roo.dd.DragDrop.
16982      */
16983     b4MouseDown: function(e) {
16984         // this.resetConstraints();
16985         this.autoOffset(e.getPageX(),
16986                             e.getPageY());
16987     },
16988
16989     /*
16990      * Event that fires prior to the onDrag event.  Overrides
16991      * Roo.dd.DragDrop.
16992      */
16993     b4Drag: function(e) {
16994         this.setDragElPos(e.getPageX(),
16995                             e.getPageY());
16996     },
16997
16998     toString: function() {
16999         return ("DD " + this.id);
17000     }
17001
17002     //////////////////////////////////////////////////////////////////////////
17003     // Debugging ygDragDrop events that can be overridden
17004     //////////////////////////////////////////////////////////////////////////
17005     /*
17006     startDrag: function(x, y) {
17007     },
17008
17009     onDrag: function(e) {
17010     },
17011
17012     onDragEnter: function(e, id) {
17013     },
17014
17015     onDragOver: function(e, id) {
17016     },
17017
17018     onDragOut: function(e, id) {
17019     },
17020
17021     onDragDrop: function(e, id) {
17022     },
17023
17024     endDrag: function(e) {
17025     }
17026
17027     */
17028
17029 });/*
17030  * Based on:
17031  * Ext JS Library 1.1.1
17032  * Copyright(c) 2006-2007, Ext JS, LLC.
17033  *
17034  * Originally Released Under LGPL - original licence link has changed is not relivant.
17035  *
17036  * Fork - LGPL
17037  * <script type="text/javascript">
17038  */
17039
17040 /**
17041  * @class Roo.dd.DDProxy
17042  * A DragDrop implementation that inserts an empty, bordered div into
17043  * the document that follows the cursor during drag operations.  At the time of
17044  * the click, the frame div is resized to the dimensions of the linked html
17045  * element, and moved to the exact location of the linked element.
17046  *
17047  * References to the "frame" element refer to the single proxy element that
17048  * was created to be dragged in place of all DDProxy elements on the
17049  * page.
17050  *
17051  * @extends Roo.dd.DD
17052  * @constructor
17053  * @param {String} id the id of the linked html element
17054  * @param {String} sGroup the group of related DragDrop objects
17055  * @param {object} config an object containing configurable attributes
17056  *                Valid properties for DDProxy in addition to those in DragDrop:
17057  *                   resizeFrame, centerFrame, dragElId
17058  */
17059 Roo.dd.DDProxy = function(id, sGroup, config) {
17060     if (id) {
17061         this.init(id, sGroup, config);
17062         this.initFrame();
17063     }
17064 };
17065
17066 /**
17067  * The default drag frame div id
17068  * @property Roo.dd.DDProxy.dragElId
17069  * @type String
17070  * @static
17071  */
17072 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17073
17074 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17075
17076     /**
17077      * By default we resize the drag frame to be the same size as the element
17078      * we want to drag (this is to get the frame effect).  We can turn it off
17079      * if we want a different behavior.
17080      * @property resizeFrame
17081      * @type boolean
17082      */
17083     resizeFrame: true,
17084
17085     /**
17086      * By default the frame is positioned exactly where the drag element is, so
17087      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17088      * you do not have constraints on the obj is to have the drag frame centered
17089      * around the cursor.  Set centerFrame to true for this effect.
17090      * @property centerFrame
17091      * @type boolean
17092      */
17093     centerFrame: false,
17094
17095     /**
17096      * Creates the proxy element if it does not yet exist
17097      * @method createFrame
17098      */
17099     createFrame: function() {
17100         var self = this;
17101         var body = document.body;
17102
17103         if (!body || !body.firstChild) {
17104             setTimeout( function() { self.createFrame(); }, 50 );
17105             return;
17106         }
17107
17108         var div = this.getDragEl();
17109
17110         if (!div) {
17111             div    = document.createElement("div");
17112             div.id = this.dragElId;
17113             var s  = div.style;
17114
17115             s.position   = "absolute";
17116             s.visibility = "hidden";
17117             s.cursor     = "move";
17118             s.border     = "2px solid #aaa";
17119             s.zIndex     = 999;
17120
17121             // appendChild can blow up IE if invoked prior to the window load event
17122             // while rendering a table.  It is possible there are other scenarios
17123             // that would cause this to happen as well.
17124             body.insertBefore(div, body.firstChild);
17125         }
17126     },
17127
17128     /**
17129      * Initialization for the drag frame element.  Must be called in the
17130      * constructor of all subclasses
17131      * @method initFrame
17132      */
17133     initFrame: function() {
17134         this.createFrame();
17135     },
17136
17137     applyConfig: function() {
17138         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17139
17140         this.resizeFrame = (this.config.resizeFrame !== false);
17141         this.centerFrame = (this.config.centerFrame);
17142         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17143     },
17144
17145     /**
17146      * Resizes the drag frame to the dimensions of the clicked object, positions
17147      * it over the object, and finally displays it
17148      * @method showFrame
17149      * @param {int} iPageX X click position
17150      * @param {int} iPageY Y click position
17151      * @private
17152      */
17153     showFrame: function(iPageX, iPageY) {
17154         var el = this.getEl();
17155         var dragEl = this.getDragEl();
17156         var s = dragEl.style;
17157
17158         this._resizeProxy();
17159
17160         if (this.centerFrame) {
17161             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17162                            Math.round(parseInt(s.height, 10)/2) );
17163         }
17164
17165         this.setDragElPos(iPageX, iPageY);
17166
17167         Roo.fly(dragEl).show();
17168     },
17169
17170     /**
17171      * The proxy is automatically resized to the dimensions of the linked
17172      * element when a drag is initiated, unless resizeFrame is set to false
17173      * @method _resizeProxy
17174      * @private
17175      */
17176     _resizeProxy: function() {
17177         if (this.resizeFrame) {
17178             var el = this.getEl();
17179             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17180         }
17181     },
17182
17183     // overrides Roo.dd.DragDrop
17184     b4MouseDown: function(e) {
17185         var x = e.getPageX();
17186         var y = e.getPageY();
17187         this.autoOffset(x, y);
17188         this.setDragElPos(x, y);
17189     },
17190
17191     // overrides Roo.dd.DragDrop
17192     b4StartDrag: function(x, y) {
17193         // show the drag frame
17194         this.showFrame(x, y);
17195     },
17196
17197     // overrides Roo.dd.DragDrop
17198     b4EndDrag: function(e) {
17199         Roo.fly(this.getDragEl()).hide();
17200     },
17201
17202     // overrides Roo.dd.DragDrop
17203     // By default we try to move the element to the last location of the frame.
17204     // This is so that the default behavior mirrors that of Roo.dd.DD.
17205     endDrag: function(e) {
17206
17207         var lel = this.getEl();
17208         var del = this.getDragEl();
17209
17210         // Show the drag frame briefly so we can get its position
17211         del.style.visibility = "";
17212
17213         this.beforeMove();
17214         // Hide the linked element before the move to get around a Safari
17215         // rendering bug.
17216         lel.style.visibility = "hidden";
17217         Roo.dd.DDM.moveToEl(lel, del);
17218         del.style.visibility = "hidden";
17219         lel.style.visibility = "";
17220
17221         this.afterDrag();
17222     },
17223
17224     beforeMove : function(){
17225
17226     },
17227
17228     afterDrag : function(){
17229
17230     },
17231
17232     toString: function() {
17233         return ("DDProxy " + this.id);
17234     }
17235
17236 });
17237 /*
17238  * Based on:
17239  * Ext JS Library 1.1.1
17240  * Copyright(c) 2006-2007, Ext JS, LLC.
17241  *
17242  * Originally Released Under LGPL - original licence link has changed is not relivant.
17243  *
17244  * Fork - LGPL
17245  * <script type="text/javascript">
17246  */
17247
17248  /**
17249  * @class Roo.dd.DDTarget
17250  * A DragDrop implementation that does not move, but can be a drop
17251  * target.  You would get the same result by simply omitting implementation
17252  * for the event callbacks, but this way we reduce the processing cost of the
17253  * event listener and the callbacks.
17254  * @extends Roo.dd.DragDrop
17255  * @constructor
17256  * @param {String} id the id of the element that is a drop target
17257  * @param {String} sGroup the group of related DragDrop objects
17258  * @param {object} config an object containing configurable attributes
17259  *                 Valid properties for DDTarget in addition to those in
17260  *                 DragDrop:
17261  *                    none
17262  */
17263 Roo.dd.DDTarget = function(id, sGroup, config) {
17264     if (id) {
17265         this.initTarget(id, sGroup, config);
17266     }
17267 };
17268
17269 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17270 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17271     toString: function() {
17272         return ("DDTarget " + this.id);
17273     }
17274 });
17275 /*
17276  * Based on:
17277  * Ext JS Library 1.1.1
17278  * Copyright(c) 2006-2007, Ext JS, LLC.
17279  *
17280  * Originally Released Under LGPL - original licence link has changed is not relivant.
17281  *
17282  * Fork - LGPL
17283  * <script type="text/javascript">
17284  */
17285  
17286
17287 /**
17288  * @class Roo.dd.ScrollManager
17289  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17290  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17291  * @singleton
17292  */
17293 Roo.dd.ScrollManager = function(){
17294     var ddm = Roo.dd.DragDropMgr;
17295     var els = {};
17296     var dragEl = null;
17297     var proc = {};
17298     
17299     var onStop = function(e){
17300         dragEl = null;
17301         clearProc();
17302     };
17303     
17304     var triggerRefresh = function(){
17305         if(ddm.dragCurrent){
17306              ddm.refreshCache(ddm.dragCurrent.groups);
17307         }
17308     };
17309     
17310     var doScroll = function(){
17311         if(ddm.dragCurrent){
17312             var dds = Roo.dd.ScrollManager;
17313             if(!dds.animate){
17314                 if(proc.el.scroll(proc.dir, dds.increment)){
17315                     triggerRefresh();
17316                 }
17317             }else{
17318                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17319             }
17320         }
17321     };
17322     
17323     var clearProc = function(){
17324         if(proc.id){
17325             clearInterval(proc.id);
17326         }
17327         proc.id = 0;
17328         proc.el = null;
17329         proc.dir = "";
17330     };
17331     
17332     var startProc = function(el, dir){
17333         clearProc();
17334         proc.el = el;
17335         proc.dir = dir;
17336         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17337     };
17338     
17339     var onFire = function(e, isDrop){
17340         if(isDrop || !ddm.dragCurrent){ return; }
17341         var dds = Roo.dd.ScrollManager;
17342         if(!dragEl || dragEl != ddm.dragCurrent){
17343             dragEl = ddm.dragCurrent;
17344             // refresh regions on drag start
17345             dds.refreshCache();
17346         }
17347         
17348         var xy = Roo.lib.Event.getXY(e);
17349         var pt = new Roo.lib.Point(xy[0], xy[1]);
17350         for(var id in els){
17351             var el = els[id], r = el._region;
17352             if(r && r.contains(pt) && el.isScrollable()){
17353                 if(r.bottom - pt.y <= dds.thresh){
17354                     if(proc.el != el){
17355                         startProc(el, "down");
17356                     }
17357                     return;
17358                 }else if(r.right - pt.x <= dds.thresh){
17359                     if(proc.el != el){
17360                         startProc(el, "left");
17361                     }
17362                     return;
17363                 }else if(pt.y - r.top <= dds.thresh){
17364                     if(proc.el != el){
17365                         startProc(el, "up");
17366                     }
17367                     return;
17368                 }else if(pt.x - r.left <= dds.thresh){
17369                     if(proc.el != el){
17370                         startProc(el, "right");
17371                     }
17372                     return;
17373                 }
17374             }
17375         }
17376         clearProc();
17377     };
17378     
17379     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17380     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17381     
17382     return {
17383         /**
17384          * Registers new overflow element(s) to auto scroll
17385          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17386          */
17387         register : function(el){
17388             if(el instanceof Array){
17389                 for(var i = 0, len = el.length; i < len; i++) {
17390                         this.register(el[i]);
17391                 }
17392             }else{
17393                 el = Roo.get(el);
17394                 els[el.id] = el;
17395             }
17396         },
17397         
17398         /**
17399          * Unregisters overflow element(s) so they are no longer scrolled
17400          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17401          */
17402         unregister : function(el){
17403             if(el instanceof Array){
17404                 for(var i = 0, len = el.length; i < len; i++) {
17405                         this.unregister(el[i]);
17406                 }
17407             }else{
17408                 el = Roo.get(el);
17409                 delete els[el.id];
17410             }
17411         },
17412         
17413         /**
17414          * The number of pixels from the edge of a container the pointer needs to be to 
17415          * trigger scrolling (defaults to 25)
17416          * @type Number
17417          */
17418         thresh : 25,
17419         
17420         /**
17421          * The number of pixels to scroll in each scroll increment (defaults to 50)
17422          * @type Number
17423          */
17424         increment : 100,
17425         
17426         /**
17427          * The frequency of scrolls in milliseconds (defaults to 500)
17428          * @type Number
17429          */
17430         frequency : 500,
17431         
17432         /**
17433          * True to animate the scroll (defaults to true)
17434          * @type Boolean
17435          */
17436         animate: true,
17437         
17438         /**
17439          * The animation duration in seconds - 
17440          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17441          * @type Number
17442          */
17443         animDuration: .4,
17444         
17445         /**
17446          * Manually trigger a cache refresh.
17447          */
17448         refreshCache : function(){
17449             for(var id in els){
17450                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17451                     els[id]._region = els[id].getRegion();
17452                 }
17453             }
17454         }
17455     };
17456 }();/*
17457  * Based on:
17458  * Ext JS Library 1.1.1
17459  * Copyright(c) 2006-2007, Ext JS, LLC.
17460  *
17461  * Originally Released Under LGPL - original licence link has changed is not relivant.
17462  *
17463  * Fork - LGPL
17464  * <script type="text/javascript">
17465  */
17466  
17467
17468 /**
17469  * @class Roo.dd.Registry
17470  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17471  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17472  * @singleton
17473  */
17474 Roo.dd.Registry = function(){
17475     var elements = {}; 
17476     var handles = {}; 
17477     var autoIdSeed = 0;
17478
17479     var getId = function(el, autogen){
17480         if(typeof el == "string"){
17481             return el;
17482         }
17483         var id = el.id;
17484         if(!id && autogen !== false){
17485             id = "roodd-" + (++autoIdSeed);
17486             el.id = id;
17487         }
17488         return id;
17489     };
17490     
17491     return {
17492     /**
17493      * Register a drag drop element
17494      * @param {String|HTMLElement} element The id or DOM node to register
17495      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17496      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17497      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17498      * populated in the data object (if applicable):
17499      * <pre>
17500 Value      Description<br />
17501 ---------  ------------------------------------------<br />
17502 handles    Array of DOM nodes that trigger dragging<br />
17503            for the element being registered<br />
17504 isHandle   True if the element passed in triggers<br />
17505            dragging itself, else false
17506 </pre>
17507      */
17508         register : function(el, data){
17509             data = data || {};
17510             if(typeof el == "string"){
17511                 el = document.getElementById(el);
17512             }
17513             data.ddel = el;
17514             elements[getId(el)] = data;
17515             if(data.isHandle !== false){
17516                 handles[data.ddel.id] = data;
17517             }
17518             if(data.handles){
17519                 var hs = data.handles;
17520                 for(var i = 0, len = hs.length; i < len; i++){
17521                         handles[getId(hs[i])] = data;
17522                 }
17523             }
17524         },
17525
17526     /**
17527      * Unregister a drag drop element
17528      * @param {String|HTMLElement}  element The id or DOM node to unregister
17529      */
17530         unregister : function(el){
17531             var id = getId(el, false);
17532             var data = elements[id];
17533             if(data){
17534                 delete elements[id];
17535                 if(data.handles){
17536                     var hs = data.handles;
17537                     for(var i = 0, len = hs.length; i < len; i++){
17538                         delete handles[getId(hs[i], false)];
17539                     }
17540                 }
17541             }
17542         },
17543
17544     /**
17545      * Returns the handle registered for a DOM Node by id
17546      * @param {String|HTMLElement} id The DOM node or id to look up
17547      * @return {Object} handle The custom handle data
17548      */
17549         getHandle : function(id){
17550             if(typeof id != "string"){ // must be element?
17551                 id = id.id;
17552             }
17553             return handles[id];
17554         },
17555
17556     /**
17557      * Returns the handle that is registered for the DOM node that is the target of the event
17558      * @param {Event} e The event
17559      * @return {Object} handle The custom handle data
17560      */
17561         getHandleFromEvent : function(e){
17562             var t = Roo.lib.Event.getTarget(e);
17563             return t ? handles[t.id] : null;
17564         },
17565
17566     /**
17567      * Returns a custom data object that is registered for a DOM node by id
17568      * @param {String|HTMLElement} id The DOM node or id to look up
17569      * @return {Object} data The custom data
17570      */
17571         getTarget : function(id){
17572             if(typeof id != "string"){ // must be element?
17573                 id = id.id;
17574             }
17575             return elements[id];
17576         },
17577
17578     /**
17579      * Returns a custom data object that is registered for the DOM node that is the target of the event
17580      * @param {Event} e The event
17581      * @return {Object} data The custom data
17582      */
17583         getTargetFromEvent : function(e){
17584             var t = Roo.lib.Event.getTarget(e);
17585             return t ? elements[t.id] || handles[t.id] : null;
17586         }
17587     };
17588 }();/*
17589  * Based on:
17590  * Ext JS Library 1.1.1
17591  * Copyright(c) 2006-2007, Ext JS, LLC.
17592  *
17593  * Originally Released Under LGPL - original licence link has changed is not relivant.
17594  *
17595  * Fork - LGPL
17596  * <script type="text/javascript">
17597  */
17598  
17599
17600 /**
17601  * @class Roo.dd.StatusProxy
17602  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17603  * default drag proxy used by all Roo.dd components.
17604  * @constructor
17605  * @param {Object} config
17606  */
17607 Roo.dd.StatusProxy = function(config){
17608     Roo.apply(this, config);
17609     this.id = this.id || Roo.id();
17610     this.el = new Roo.Layer({
17611         dh: {
17612             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17613                 {tag: "div", cls: "x-dd-drop-icon"},
17614                 {tag: "div", cls: "x-dd-drag-ghost"}
17615             ]
17616         }, 
17617         shadow: !config || config.shadow !== false
17618     });
17619     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17620     this.dropStatus = this.dropNotAllowed;
17621 };
17622
17623 Roo.dd.StatusProxy.prototype = {
17624     /**
17625      * @cfg {String} dropAllowed
17626      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17627      */
17628     dropAllowed : "x-dd-drop-ok",
17629     /**
17630      * @cfg {String} dropNotAllowed
17631      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17632      */
17633     dropNotAllowed : "x-dd-drop-nodrop",
17634
17635     /**
17636      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17637      * over the current target element.
17638      * @param {String} cssClass The css class for the new drop status indicator image
17639      */
17640     setStatus : function(cssClass){
17641         cssClass = cssClass || this.dropNotAllowed;
17642         if(this.dropStatus != cssClass){
17643             this.el.replaceClass(this.dropStatus, cssClass);
17644             this.dropStatus = cssClass;
17645         }
17646     },
17647
17648     /**
17649      * Resets the status indicator to the default dropNotAllowed value
17650      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17651      */
17652     reset : function(clearGhost){
17653         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17654         this.dropStatus = this.dropNotAllowed;
17655         if(clearGhost){
17656             this.ghost.update("");
17657         }
17658     },
17659
17660     /**
17661      * Updates the contents of the ghost element
17662      * @param {String} html The html that will replace the current innerHTML of the ghost element
17663      */
17664     update : function(html){
17665         if(typeof html == "string"){
17666             this.ghost.update(html);
17667         }else{
17668             this.ghost.update("");
17669             html.style.margin = "0";
17670             this.ghost.dom.appendChild(html);
17671         }
17672         // ensure float = none set?? cant remember why though.
17673         var el = this.ghost.dom.firstChild;
17674                 if(el){
17675                         Roo.fly(el).setStyle('float', 'none');
17676                 }
17677     },
17678     
17679     /**
17680      * Returns the underlying proxy {@link Roo.Layer}
17681      * @return {Roo.Layer} el
17682     */
17683     getEl : function(){
17684         return this.el;
17685     },
17686
17687     /**
17688      * Returns the ghost element
17689      * @return {Roo.Element} el
17690      */
17691     getGhost : function(){
17692         return this.ghost;
17693     },
17694
17695     /**
17696      * Hides the proxy
17697      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17698      */
17699     hide : function(clear){
17700         this.el.hide();
17701         if(clear){
17702             this.reset(true);
17703         }
17704     },
17705
17706     /**
17707      * Stops the repair animation if it's currently running
17708      */
17709     stop : function(){
17710         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17711             this.anim.stop();
17712         }
17713     },
17714
17715     /**
17716      * Displays this proxy
17717      */
17718     show : function(){
17719         this.el.show();
17720     },
17721
17722     /**
17723      * Force the Layer to sync its shadow and shim positions to the element
17724      */
17725     sync : function(){
17726         this.el.sync();
17727     },
17728
17729     /**
17730      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17731      * invalid drop operation by the item being dragged.
17732      * @param {Array} xy The XY position of the element ([x, y])
17733      * @param {Function} callback The function to call after the repair is complete
17734      * @param {Object} scope The scope in which to execute the callback
17735      */
17736     repair : function(xy, callback, scope){
17737         this.callback = callback;
17738         this.scope = scope;
17739         if(xy && this.animRepair !== false){
17740             this.el.addClass("x-dd-drag-repair");
17741             this.el.hideUnders(true);
17742             this.anim = this.el.shift({
17743                 duration: this.repairDuration || .5,
17744                 easing: 'easeOut',
17745                 xy: xy,
17746                 stopFx: true,
17747                 callback: this.afterRepair,
17748                 scope: this
17749             });
17750         }else{
17751             this.afterRepair();
17752         }
17753     },
17754
17755     // private
17756     afterRepair : function(){
17757         this.hide(true);
17758         if(typeof this.callback == "function"){
17759             this.callback.call(this.scope || this);
17760         }
17761         this.callback = null;
17762         this.scope = null;
17763     }
17764 };/*
17765  * Based on:
17766  * Ext JS Library 1.1.1
17767  * Copyright(c) 2006-2007, Ext JS, LLC.
17768  *
17769  * Originally Released Under LGPL - original licence link has changed is not relivant.
17770  *
17771  * Fork - LGPL
17772  * <script type="text/javascript">
17773  */
17774
17775 /**
17776  * @class Roo.dd.DragSource
17777  * @extends Roo.dd.DDProxy
17778  * A simple class that provides the basic implementation needed to make any element draggable.
17779  * @constructor
17780  * @param {String/HTMLElement/Element} el The container element
17781  * @param {Object} config
17782  */
17783 Roo.dd.DragSource = function(el, config){
17784     this.el = Roo.get(el);
17785     this.dragData = {};
17786     
17787     Roo.apply(this, config);
17788     
17789     if(!this.proxy){
17790         this.proxy = new Roo.dd.StatusProxy();
17791     }
17792
17793     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17794           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17795     
17796     this.dragging = false;
17797 };
17798
17799 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17800     /**
17801      * @cfg {String} dropAllowed
17802      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17803      */
17804     dropAllowed : "x-dd-drop-ok",
17805     /**
17806      * @cfg {String} dropNotAllowed
17807      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17808      */
17809     dropNotAllowed : "x-dd-drop-nodrop",
17810
17811     /**
17812      * Returns the data object associated with this drag source
17813      * @return {Object} data An object containing arbitrary data
17814      */
17815     getDragData : function(e){
17816         return this.dragData;
17817     },
17818
17819     // private
17820     onDragEnter : function(e, id){
17821         var target = Roo.dd.DragDropMgr.getDDById(id);
17822         this.cachedTarget = target;
17823         if(this.beforeDragEnter(target, e, id) !== false){
17824             if(target.isNotifyTarget){
17825                 var status = target.notifyEnter(this, e, this.dragData);
17826                 this.proxy.setStatus(status);
17827             }else{
17828                 this.proxy.setStatus(this.dropAllowed);
17829             }
17830             
17831             if(this.afterDragEnter){
17832                 /**
17833                  * An empty function by default, but provided so that you can perform a custom action
17834                  * when the dragged item enters the drop target by providing an implementation.
17835                  * @param {Roo.dd.DragDrop} target The drop target
17836                  * @param {Event} e The event object
17837                  * @param {String} id The id of the dragged element
17838                  * @method afterDragEnter
17839                  */
17840                 this.afterDragEnter(target, e, id);
17841             }
17842         }
17843     },
17844
17845     /**
17846      * An empty function by default, but provided so that you can perform a custom action
17847      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17848      * @param {Roo.dd.DragDrop} target The drop target
17849      * @param {Event} e The event object
17850      * @param {String} id The id of the dragged element
17851      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17852      */
17853     beforeDragEnter : function(target, e, id){
17854         return true;
17855     },
17856
17857     // private
17858     alignElWithMouse: function() {
17859         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17860         this.proxy.sync();
17861     },
17862
17863     // private
17864     onDragOver : function(e, id){
17865         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17866         if(this.beforeDragOver(target, e, id) !== false){
17867             if(target.isNotifyTarget){
17868                 var status = target.notifyOver(this, e, this.dragData);
17869                 this.proxy.setStatus(status);
17870             }
17871
17872             if(this.afterDragOver){
17873                 /**
17874                  * An empty function by default, but provided so that you can perform a custom action
17875                  * while the dragged item is over the drop target by providing an implementation.
17876                  * @param {Roo.dd.DragDrop} target The drop target
17877                  * @param {Event} e The event object
17878                  * @param {String} id The id of the dragged element
17879                  * @method afterDragOver
17880                  */
17881                 this.afterDragOver(target, e, id);
17882             }
17883         }
17884     },
17885
17886     /**
17887      * An empty function by default, but provided so that you can perform a custom action
17888      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17889      * @param {Roo.dd.DragDrop} target The drop target
17890      * @param {Event} e The event object
17891      * @param {String} id The id of the dragged element
17892      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17893      */
17894     beforeDragOver : function(target, e, id){
17895         return true;
17896     },
17897
17898     // private
17899     onDragOut : function(e, id){
17900         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17901         if(this.beforeDragOut(target, e, id) !== false){
17902             if(target.isNotifyTarget){
17903                 target.notifyOut(this, e, this.dragData);
17904             }
17905             this.proxy.reset();
17906             if(this.afterDragOut){
17907                 /**
17908                  * An empty function by default, but provided so that you can perform a custom action
17909                  * after the dragged item is dragged out of the target without dropping.
17910                  * @param {Roo.dd.DragDrop} target The drop target
17911                  * @param {Event} e The event object
17912                  * @param {String} id The id of the dragged element
17913                  * @method afterDragOut
17914                  */
17915                 this.afterDragOut(target, e, id);
17916             }
17917         }
17918         this.cachedTarget = null;
17919     },
17920
17921     /**
17922      * An empty function by default, but provided so that you can perform a custom action before the dragged
17923      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17924      * @param {Roo.dd.DragDrop} target The drop target
17925      * @param {Event} e The event object
17926      * @param {String} id The id of the dragged element
17927      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17928      */
17929     beforeDragOut : function(target, e, id){
17930         return true;
17931     },
17932     
17933     // private
17934     onDragDrop : function(e, id){
17935         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17936         if(this.beforeDragDrop(target, e, id) !== false){
17937             if(target.isNotifyTarget){
17938                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17939                     this.onValidDrop(target, e, id);
17940                 }else{
17941                     this.onInvalidDrop(target, e, id);
17942                 }
17943             }else{
17944                 this.onValidDrop(target, e, id);
17945             }
17946             
17947             if(this.afterDragDrop){
17948                 /**
17949                  * An empty function by default, but provided so that you can perform a custom action
17950                  * after a valid drag drop has occurred by providing an implementation.
17951                  * @param {Roo.dd.DragDrop} target The drop target
17952                  * @param {Event} e The event object
17953                  * @param {String} id The id of the dropped element
17954                  * @method afterDragDrop
17955                  */
17956                 this.afterDragDrop(target, e, id);
17957             }
17958         }
17959         delete this.cachedTarget;
17960     },
17961
17962     /**
17963      * An empty function by default, but provided so that you can perform a custom action before the dragged
17964      * item is dropped onto the target and optionally cancel the onDragDrop.
17965      * @param {Roo.dd.DragDrop} target The drop target
17966      * @param {Event} e The event object
17967      * @param {String} id The id of the dragged element
17968      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17969      */
17970     beforeDragDrop : function(target, e, id){
17971         return true;
17972     },
17973
17974     // private
17975     onValidDrop : function(target, e, id){
17976         this.hideProxy();
17977         if(this.afterValidDrop){
17978             /**
17979              * An empty function by default, but provided so that you can perform a custom action
17980              * after a valid drop has occurred by providing an implementation.
17981              * @param {Object} target The target DD 
17982              * @param {Event} e The event object
17983              * @param {String} id The id of the dropped element
17984              * @method afterInvalidDrop
17985              */
17986             this.afterValidDrop(target, e, id);
17987         }
17988     },
17989
17990     // private
17991     getRepairXY : function(e, data){
17992         return this.el.getXY();  
17993     },
17994
17995     // private
17996     onInvalidDrop : function(target, e, id){
17997         this.beforeInvalidDrop(target, e, id);
17998         if(this.cachedTarget){
17999             if(this.cachedTarget.isNotifyTarget){
18000                 this.cachedTarget.notifyOut(this, e, this.dragData);
18001             }
18002             this.cacheTarget = null;
18003         }
18004         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18005
18006         if(this.afterInvalidDrop){
18007             /**
18008              * An empty function by default, but provided so that you can perform a custom action
18009              * after an invalid drop has occurred by providing an implementation.
18010              * @param {Event} e The event object
18011              * @param {String} id The id of the dropped element
18012              * @method afterInvalidDrop
18013              */
18014             this.afterInvalidDrop(e, id);
18015         }
18016     },
18017
18018     // private
18019     afterRepair : function(){
18020         if(Roo.enableFx){
18021             this.el.highlight(this.hlColor || "c3daf9");
18022         }
18023         this.dragging = false;
18024     },
18025
18026     /**
18027      * An empty function by default, but provided so that you can perform a custom action after an invalid
18028      * drop has occurred.
18029      * @param {Roo.dd.DragDrop} target The drop target
18030      * @param {Event} e The event object
18031      * @param {String} id The id of the dragged element
18032      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18033      */
18034     beforeInvalidDrop : function(target, e, id){
18035         return true;
18036     },
18037
18038     // private
18039     handleMouseDown : function(e){
18040         if(this.dragging) {
18041             return;
18042         }
18043         var data = this.getDragData(e);
18044         if(data && this.onBeforeDrag(data, e) !== false){
18045             this.dragData = data;
18046             this.proxy.stop();
18047             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18048         } 
18049     },
18050
18051     /**
18052      * An empty function by default, but provided so that you can perform a custom action before the initial
18053      * drag event begins and optionally cancel it.
18054      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18055      * @param {Event} e The event object
18056      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18057      */
18058     onBeforeDrag : function(data, e){
18059         return true;
18060     },
18061
18062     /**
18063      * An empty function by default, but provided so that you can perform a custom action once the initial
18064      * drag event has begun.  The drag cannot be canceled from this function.
18065      * @param {Number} x The x position of the click on the dragged object
18066      * @param {Number} y The y position of the click on the dragged object
18067      */
18068     onStartDrag : Roo.emptyFn,
18069
18070     // private - YUI override
18071     startDrag : function(x, y){
18072         this.proxy.reset();
18073         this.dragging = true;
18074         this.proxy.update("");
18075         this.onInitDrag(x, y);
18076         this.proxy.show();
18077     },
18078
18079     // private
18080     onInitDrag : function(x, y){
18081         var clone = this.el.dom.cloneNode(true);
18082         clone.id = Roo.id(); // prevent duplicate ids
18083         this.proxy.update(clone);
18084         this.onStartDrag(x, y);
18085         return true;
18086     },
18087
18088     /**
18089      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18090      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18091      */
18092     getProxy : function(){
18093         return this.proxy;  
18094     },
18095
18096     /**
18097      * Hides the drag source's {@link Roo.dd.StatusProxy}
18098      */
18099     hideProxy : function(){
18100         this.proxy.hide();  
18101         this.proxy.reset(true);
18102         this.dragging = false;
18103     },
18104
18105     // private
18106     triggerCacheRefresh : function(){
18107         Roo.dd.DDM.refreshCache(this.groups);
18108     },
18109
18110     // private - override to prevent hiding
18111     b4EndDrag: function(e) {
18112     },
18113
18114     // private - override to prevent moving
18115     endDrag : function(e){
18116         this.onEndDrag(this.dragData, e);
18117     },
18118
18119     // private
18120     onEndDrag : function(data, e){
18121     },
18122     
18123     // private - pin to cursor
18124     autoOffset : function(x, y) {
18125         this.setDelta(-12, -20);
18126     }    
18127 });/*
18128  * Based on:
18129  * Ext JS Library 1.1.1
18130  * Copyright(c) 2006-2007, Ext JS, LLC.
18131  *
18132  * Originally Released Under LGPL - original licence link has changed is not relivant.
18133  *
18134  * Fork - LGPL
18135  * <script type="text/javascript">
18136  */
18137
18138
18139 /**
18140  * @class Roo.dd.DropTarget
18141  * @extends Roo.dd.DDTarget
18142  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18143  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18144  * @constructor
18145  * @param {String/HTMLElement/Element} el The container element
18146  * @param {Object} config
18147  */
18148 Roo.dd.DropTarget = function(el, config){
18149     this.el = Roo.get(el);
18150     
18151     Roo.apply(this, config);
18152     
18153     if(this.containerScroll){
18154         Roo.dd.ScrollManager.register(this.el);
18155     }
18156     
18157     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18158           {isTarget: true});
18159
18160 };
18161
18162 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18163     /**
18164      * @cfg {String} overClass
18165      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18166      */
18167     /**
18168      * @cfg {String} dropAllowed
18169      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18170      */
18171     dropAllowed : "x-dd-drop-ok",
18172     /**
18173      * @cfg {String} dropNotAllowed
18174      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18175      */
18176     dropNotAllowed : "x-dd-drop-nodrop",
18177
18178     // private
18179     isTarget : true,
18180
18181     // private
18182     isNotifyTarget : true,
18183
18184     /**
18185      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18186      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18187      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18188      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18189      * @param {Event} e The event
18190      * @param {Object} data An object containing arbitrary data supplied by the drag source
18191      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18192      * underlying {@link Roo.dd.StatusProxy} can be updated
18193      */
18194     notifyEnter : function(dd, e, data){
18195         if(this.overClass){
18196             this.el.addClass(this.overClass);
18197         }
18198         return this.dropAllowed;
18199     },
18200
18201     /**
18202      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18203      * This method will be called on every mouse movement while the drag source is over the drop target.
18204      * This default implementation simply returns the dropAllowed config value.
18205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18206      * @param {Event} e The event
18207      * @param {Object} data An object containing arbitrary data supplied by the drag source
18208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18209      * underlying {@link Roo.dd.StatusProxy} can be updated
18210      */
18211     notifyOver : function(dd, e, data){
18212         return this.dropAllowed;
18213     },
18214
18215     /**
18216      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18217      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18218      * overClass (if any) from the drop element.
18219      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18220      * @param {Event} e The event
18221      * @param {Object} data An object containing arbitrary data supplied by the drag source
18222      */
18223     notifyOut : function(dd, e, data){
18224         if(this.overClass){
18225             this.el.removeClass(this.overClass);
18226         }
18227     },
18228
18229     /**
18230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18231      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18232      * implementation that does something to process the drop event and returns true so that the drag source's
18233      * repair action does not run.
18234      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18235      * @param {Event} e The event
18236      * @param {Object} data An object containing arbitrary data supplied by the drag source
18237      * @return {Boolean} True if the drop was valid, else false
18238      */
18239     notifyDrop : function(dd, e, data){
18240         return false;
18241     }
18242 });/*
18243  * Based on:
18244  * Ext JS Library 1.1.1
18245  * Copyright(c) 2006-2007, Ext JS, LLC.
18246  *
18247  * Originally Released Under LGPL - original licence link has changed is not relivant.
18248  *
18249  * Fork - LGPL
18250  * <script type="text/javascript">
18251  */
18252
18253
18254 /**
18255  * @class Roo.dd.DragZone
18256  * @extends Roo.dd.DragSource
18257  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18258  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18259  * @constructor
18260  * @param {String/HTMLElement/Element} el The container element
18261  * @param {Object} config
18262  */
18263 Roo.dd.DragZone = function(el, config){
18264     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18265     if(this.containerScroll){
18266         Roo.dd.ScrollManager.register(this.el);
18267     }
18268 };
18269
18270 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18271     /**
18272      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18273      * for auto scrolling during drag operations.
18274      */
18275     /**
18276      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18277      * method after a failed drop (defaults to "c3daf9" - light blue)
18278      */
18279
18280     /**
18281      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18282      * for a valid target to drag based on the mouse down. Override this method
18283      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18284      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18285      * @param {EventObject} e The mouse down event
18286      * @return {Object} The dragData
18287      */
18288     getDragData : function(e){
18289         return Roo.dd.Registry.getHandleFromEvent(e);
18290     },
18291     
18292     /**
18293      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18294      * this.dragData.ddel
18295      * @param {Number} x The x position of the click on the dragged object
18296      * @param {Number} y The y position of the click on the dragged object
18297      * @return {Boolean} true to continue the drag, false to cancel
18298      */
18299     onInitDrag : function(x, y){
18300         this.proxy.update(this.dragData.ddel.cloneNode(true));
18301         this.onStartDrag(x, y);
18302         return true;
18303     },
18304     
18305     /**
18306      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18307      */
18308     afterRepair : function(){
18309         if(Roo.enableFx){
18310             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18311         }
18312         this.dragging = false;
18313     },
18314
18315     /**
18316      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18317      * the XY of this.dragData.ddel
18318      * @param {EventObject} e The mouse up event
18319      * @return {Array} The xy location (e.g. [100, 200])
18320      */
18321     getRepairXY : function(e){
18322         return Roo.Element.fly(this.dragData.ddel).getXY();  
18323     }
18324 });/*
18325  * Based on:
18326  * Ext JS Library 1.1.1
18327  * Copyright(c) 2006-2007, Ext JS, LLC.
18328  *
18329  * Originally Released Under LGPL - original licence link has changed is not relivant.
18330  *
18331  * Fork - LGPL
18332  * <script type="text/javascript">
18333  */
18334 /**
18335  * @class Roo.dd.DropZone
18336  * @extends Roo.dd.DropTarget
18337  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18338  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18339  * @constructor
18340  * @param {String/HTMLElement/Element} el The container element
18341  * @param {Object} config
18342  */
18343 Roo.dd.DropZone = function(el, config){
18344     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18345 };
18346
18347 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18348     /**
18349      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18350      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18351      * provide your own custom lookup.
18352      * @param {Event} e The event
18353      * @return {Object} data The custom data
18354      */
18355     getTargetFromEvent : function(e){
18356         return Roo.dd.Registry.getTargetFromEvent(e);
18357     },
18358
18359     /**
18360      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18361      * that it has registered.  This method has no default implementation and should be overridden to provide
18362      * node-specific processing if necessary.
18363      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18364      * {@link #getTargetFromEvent} for this node)
18365      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18366      * @param {Event} e The event
18367      * @param {Object} data An object containing arbitrary data supplied by the drag source
18368      */
18369     onNodeEnter : function(n, dd, e, data){
18370         
18371     },
18372
18373     /**
18374      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18375      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18376      * overridden to provide the proper feedback.
18377      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18378      * {@link #getTargetFromEvent} for this node)
18379      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18380      * @param {Event} e The event
18381      * @param {Object} data An object containing arbitrary data supplied by the drag source
18382      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18383      * underlying {@link Roo.dd.StatusProxy} can be updated
18384      */
18385     onNodeOver : function(n, dd, e, data){
18386         return this.dropAllowed;
18387     },
18388
18389     /**
18390      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18391      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18392      * node-specific processing if necessary.
18393      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18394      * {@link #getTargetFromEvent} for this node)
18395      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18396      * @param {Event} e The event
18397      * @param {Object} data An object containing arbitrary data supplied by the drag source
18398      */
18399     onNodeOut : function(n, dd, e, data){
18400         
18401     },
18402
18403     /**
18404      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18405      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18406      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18407      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18408      * {@link #getTargetFromEvent} for this node)
18409      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18410      * @param {Event} e The event
18411      * @param {Object} data An object containing arbitrary data supplied by the drag source
18412      * @return {Boolean} True if the drop was valid, else false
18413      */
18414     onNodeDrop : function(n, dd, e, data){
18415         return false;
18416     },
18417
18418     /**
18419      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18420      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18421      * it should be overridden to provide the proper feedback if necessary.
18422      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18423      * @param {Event} e The event
18424      * @param {Object} data An object containing arbitrary data supplied by the drag source
18425      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18426      * underlying {@link Roo.dd.StatusProxy} can be updated
18427      */
18428     onContainerOver : function(dd, e, data){
18429         return this.dropNotAllowed;
18430     },
18431
18432     /**
18433      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18434      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18435      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18436      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18437      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18438      * @param {Event} e The event
18439      * @param {Object} data An object containing arbitrary data supplied by the drag source
18440      * @return {Boolean} True if the drop was valid, else false
18441      */
18442     onContainerDrop : function(dd, e, data){
18443         return false;
18444     },
18445
18446     /**
18447      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18448      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18449      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18450      * you should override this method and provide a custom implementation.
18451      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18452      * @param {Event} e The event
18453      * @param {Object} data An object containing arbitrary data supplied by the drag source
18454      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18455      * underlying {@link Roo.dd.StatusProxy} can be updated
18456      */
18457     notifyEnter : function(dd, e, data){
18458         return this.dropNotAllowed;
18459     },
18460
18461     /**
18462      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18463      * This method will be called on every mouse movement while the drag source is over the drop zone.
18464      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18465      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18466      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18467      * registered node, it will call {@link #onContainerOver}.
18468      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18469      * @param {Event} e The event
18470      * @param {Object} data An object containing arbitrary data supplied by the drag source
18471      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18472      * underlying {@link Roo.dd.StatusProxy} can be updated
18473      */
18474     notifyOver : function(dd, e, data){
18475         var n = this.getTargetFromEvent(e);
18476         if(!n){ // not over valid drop target
18477             if(this.lastOverNode){
18478                 this.onNodeOut(this.lastOverNode, dd, e, data);
18479                 this.lastOverNode = null;
18480             }
18481             return this.onContainerOver(dd, e, data);
18482         }
18483         if(this.lastOverNode != n){
18484             if(this.lastOverNode){
18485                 this.onNodeOut(this.lastOverNode, dd, e, data);
18486             }
18487             this.onNodeEnter(n, dd, e, data);
18488             this.lastOverNode = n;
18489         }
18490         return this.onNodeOver(n, dd, e, data);
18491     },
18492
18493     /**
18494      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18495      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18496      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18497      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18498      * @param {Event} e The event
18499      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18500      */
18501     notifyOut : function(dd, e, data){
18502         if(this.lastOverNode){
18503             this.onNodeOut(this.lastOverNode, dd, e, data);
18504             this.lastOverNode = null;
18505         }
18506     },
18507
18508     /**
18509      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18510      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18511      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18512      * otherwise it will call {@link #onContainerDrop}.
18513      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18514      * @param {Event} e The event
18515      * @param {Object} data An object containing arbitrary data supplied by the drag source
18516      * @return {Boolean} True if the drop was valid, else false
18517      */
18518     notifyDrop : function(dd, e, data){
18519         if(this.lastOverNode){
18520             this.onNodeOut(this.lastOverNode, dd, e, data);
18521             this.lastOverNode = null;
18522         }
18523         var n = this.getTargetFromEvent(e);
18524         return n ?
18525             this.onNodeDrop(n, dd, e, data) :
18526             this.onContainerDrop(dd, e, data);
18527     },
18528
18529     // private
18530     triggerCacheRefresh : function(){
18531         Roo.dd.DDM.refreshCache(this.groups);
18532     }  
18533 });/*
18534  * Based on:
18535  * Ext JS Library 1.1.1
18536  * Copyright(c) 2006-2007, Ext JS, LLC.
18537  *
18538  * Originally Released Under LGPL - original licence link has changed is not relivant.
18539  *
18540  * Fork - LGPL
18541  * <script type="text/javascript">
18542  */
18543
18544
18545 /**
18546  * @class Roo.data.SortTypes
18547  * @singleton
18548  * Defines the default sorting (casting?) comparison functions used when sorting data.
18549  */
18550 Roo.data.SortTypes = {
18551     /**
18552      * Default sort that does nothing
18553      * @param {Mixed} s The value being converted
18554      * @return {Mixed} The comparison value
18555      */
18556     none : function(s){
18557         return s;
18558     },
18559     
18560     /**
18561      * The regular expression used to strip tags
18562      * @type {RegExp}
18563      * @property
18564      */
18565     stripTagsRE : /<\/?[^>]+>/gi,
18566     
18567     /**
18568      * Strips all HTML tags to sort on text only
18569      * @param {Mixed} s The value being converted
18570      * @return {String} The comparison value
18571      */
18572     asText : function(s){
18573         return String(s).replace(this.stripTagsRE, "");
18574     },
18575     
18576     /**
18577      * Strips all HTML tags to sort on text only - Case insensitive
18578      * @param {Mixed} s The value being converted
18579      * @return {String} The comparison value
18580      */
18581     asUCText : function(s){
18582         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18583     },
18584     
18585     /**
18586      * Case insensitive string
18587      * @param {Mixed} s The value being converted
18588      * @return {String} The comparison value
18589      */
18590     asUCString : function(s) {
18591         return String(s).toUpperCase();
18592     },
18593     
18594     /**
18595      * Date sorting
18596      * @param {Mixed} s The value being converted
18597      * @return {Number} The comparison value
18598      */
18599     asDate : function(s) {
18600         if(!s){
18601             return 0;
18602         }
18603         if(s instanceof Date){
18604             return s.getTime();
18605         }
18606         return Date.parse(String(s));
18607     },
18608     
18609     /**
18610      * Float sorting
18611      * @param {Mixed} s The value being converted
18612      * @return {Float} The comparison value
18613      */
18614     asFloat : function(s) {
18615         var val = parseFloat(String(s).replace(/,/g, ""));
18616         if(isNaN(val)) val = 0;
18617         return val;
18618     },
18619     
18620     /**
18621      * Integer sorting
18622      * @param {Mixed} s The value being converted
18623      * @return {Number} The comparison value
18624      */
18625     asInt : function(s) {
18626         var val = parseInt(String(s).replace(/,/g, ""));
18627         if(isNaN(val)) val = 0;
18628         return val;
18629     }
18630 };/*
18631  * Based on:
18632  * Ext JS Library 1.1.1
18633  * Copyright(c) 2006-2007, Ext JS, LLC.
18634  *
18635  * Originally Released Under LGPL - original licence link has changed is not relivant.
18636  *
18637  * Fork - LGPL
18638  * <script type="text/javascript">
18639  */
18640
18641 /**
18642 * @class Roo.data.Record
18643  * Instances of this class encapsulate both record <em>definition</em> information, and record
18644  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18645  * to access Records cached in an {@link Roo.data.Store} object.<br>
18646  * <p>
18647  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18648  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18649  * objects.<br>
18650  * <p>
18651  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18652  * @constructor
18653  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18654  * {@link #create}. The parameters are the same.
18655  * @param {Array} data An associative Array of data values keyed by the field name.
18656  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18657  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18658  * not specified an integer id is generated.
18659  */
18660 Roo.data.Record = function(data, id){
18661     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18662     this.data = data;
18663 };
18664
18665 /**
18666  * Generate a constructor for a specific record layout.
18667  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18668  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18669  * Each field definition object may contain the following properties: <ul>
18670  * <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,
18671  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18672  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18673  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18674  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18675  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18676  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18677  * this may be omitted.</p></li>
18678  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18679  * <ul><li>auto (Default, implies no conversion)</li>
18680  * <li>string</li>
18681  * <li>int</li>
18682  * <li>float</li>
18683  * <li>boolean</li>
18684  * <li>date</li></ul></p></li>
18685  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18686  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18687  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18688  * by the Reader into an object that will be stored in the Record. It is passed the
18689  * following parameters:<ul>
18690  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18691  * </ul></p></li>
18692  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18693  * </ul>
18694  * <br>usage:<br><pre><code>
18695 var TopicRecord = Roo.data.Record.create(
18696     {name: 'title', mapping: 'topic_title'},
18697     {name: 'author', mapping: 'username'},
18698     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18699     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18700     {name: 'lastPoster', mapping: 'user2'},
18701     {name: 'excerpt', mapping: 'post_text'}
18702 );
18703
18704 var myNewRecord = new TopicRecord({
18705     title: 'Do my job please',
18706     author: 'noobie',
18707     totalPosts: 1,
18708     lastPost: new Date(),
18709     lastPoster: 'Animal',
18710     excerpt: 'No way dude!'
18711 });
18712 myStore.add(myNewRecord);
18713 </code></pre>
18714  * @method create
18715  * @static
18716  */
18717 Roo.data.Record.create = function(o){
18718     var f = function(){
18719         f.superclass.constructor.apply(this, arguments);
18720     };
18721     Roo.extend(f, Roo.data.Record);
18722     var p = f.prototype;
18723     p.fields = new Roo.util.MixedCollection(false, function(field){
18724         return field.name;
18725     });
18726     for(var i = 0, len = o.length; i < len; i++){
18727         p.fields.add(new Roo.data.Field(o[i]));
18728     }
18729     f.getField = function(name){
18730         return p.fields.get(name);  
18731     };
18732     return f;
18733 };
18734
18735 Roo.data.Record.AUTO_ID = 1000;
18736 Roo.data.Record.EDIT = 'edit';
18737 Roo.data.Record.REJECT = 'reject';
18738 Roo.data.Record.COMMIT = 'commit';
18739
18740 Roo.data.Record.prototype = {
18741     /**
18742      * Readonly flag - true if this record has been modified.
18743      * @type Boolean
18744      */
18745     dirty : false,
18746     editing : false,
18747     error: null,
18748     modified: null,
18749
18750     // private
18751     join : function(store){
18752         this.store = store;
18753     },
18754
18755     /**
18756      * Set the named field to the specified value.
18757      * @param {String} name The name of the field to set.
18758      * @param {Object} value The value to set the field to.
18759      */
18760     set : function(name, value){
18761         if(this.data[name] == value){
18762             return;
18763         }
18764         this.dirty = true;
18765         if(!this.modified){
18766             this.modified = {};
18767         }
18768         if(typeof this.modified[name] == 'undefined'){
18769             this.modified[name] = this.data[name];
18770         }
18771         this.data[name] = value;
18772         if(!this.editing){
18773             this.store.afterEdit(this);
18774         }       
18775     },
18776
18777     /**
18778      * Get the value of the named field.
18779      * @param {String} name The name of the field to get the value of.
18780      * @return {Object} The value of the field.
18781      */
18782     get : function(name){
18783         return this.data[name]; 
18784     },
18785
18786     // private
18787     beginEdit : function(){
18788         this.editing = true;
18789         this.modified = {}; 
18790     },
18791
18792     // private
18793     cancelEdit : function(){
18794         this.editing = false;
18795         delete this.modified;
18796     },
18797
18798     // private
18799     endEdit : function(){
18800         this.editing = false;
18801         if(this.dirty && this.store){
18802             this.store.afterEdit(this);
18803         }
18804     },
18805
18806     /**
18807      * Usually called by the {@link Roo.data.Store} which owns the Record.
18808      * Rejects all changes made to the Record since either creation, or the last commit operation.
18809      * Modified fields are reverted to their original values.
18810      * <p>
18811      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18812      * of reject operations.
18813      */
18814     reject : function(){
18815         var m = this.modified;
18816         for(var n in m){
18817             if(typeof m[n] != "function"){
18818                 this.data[n] = m[n];
18819             }
18820         }
18821         this.dirty = false;
18822         delete this.modified;
18823         this.editing = false;
18824         if(this.store){
18825             this.store.afterReject(this);
18826         }
18827     },
18828
18829     /**
18830      * Usually called by the {@link Roo.data.Store} which owns the Record.
18831      * Commits all changes made to the Record since either creation, or the last commit operation.
18832      * <p>
18833      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18834      * of commit operations.
18835      */
18836     commit : function(){
18837         this.dirty = false;
18838         delete this.modified;
18839         this.editing = false;
18840         if(this.store){
18841             this.store.afterCommit(this);
18842         }
18843     },
18844
18845     // private
18846     hasError : function(){
18847         return this.error != null;
18848     },
18849
18850     // private
18851     clearError : function(){
18852         this.error = null;
18853     },
18854
18855     /**
18856      * Creates a copy of this record.
18857      * @param {String} id (optional) A new record id if you don't want to use this record's id
18858      * @return {Record}
18859      */
18860     copy : function(newId) {
18861         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18862     }
18863 };/*
18864  * Based on:
18865  * Ext JS Library 1.1.1
18866  * Copyright(c) 2006-2007, Ext JS, LLC.
18867  *
18868  * Originally Released Under LGPL - original licence link has changed is not relivant.
18869  *
18870  * Fork - LGPL
18871  * <script type="text/javascript">
18872  */
18873
18874
18875
18876 /**
18877  * @class Roo.data.Store
18878  * @extends Roo.util.Observable
18879  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18880  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18881  * <p>
18882  * 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
18883  * has no knowledge of the format of the data returned by the Proxy.<br>
18884  * <p>
18885  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18886  * instances from the data object. These records are cached and made available through accessor functions.
18887  * @constructor
18888  * Creates a new Store.
18889  * @param {Object} config A config object containing the objects needed for the Store to access data,
18890  * and read the data into Records.
18891  */
18892 Roo.data.Store = function(config){
18893     this.data = new Roo.util.MixedCollection(false);
18894     this.data.getKey = function(o){
18895         return o.id;
18896     };
18897     this.baseParams = {};
18898     // private
18899     this.paramNames = {
18900         "start" : "start",
18901         "limit" : "limit",
18902         "sort" : "sort",
18903         "dir" : "dir"
18904     };
18905
18906     if(config && config.data){
18907         this.inlineData = config.data;
18908         delete config.data;
18909     }
18910
18911     Roo.apply(this, config);
18912     
18913     if(this.reader){ // reader passed
18914         this.reader = Roo.factory(this.reader, Roo.data);
18915         this.reader.xmodule = this.xmodule || false;
18916         if(!this.recordType){
18917             this.recordType = this.reader.recordType;
18918         }
18919         if(this.reader.onMetaChange){
18920             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18921         }
18922     }
18923
18924     if(this.recordType){
18925         this.fields = this.recordType.prototype.fields;
18926     }
18927     this.modified = [];
18928
18929     this.addEvents({
18930         /**
18931          * @event datachanged
18932          * Fires when the data cache has changed, and a widget which is using this Store
18933          * as a Record cache should refresh its view.
18934          * @param {Store} this
18935          */
18936         datachanged : true,
18937         /**
18938          * @event metachange
18939          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18940          * @param {Store} this
18941          * @param {Object} meta The JSON metadata
18942          */
18943         metachange : true,
18944         /**
18945          * @event add
18946          * Fires when Records have been added to the Store
18947          * @param {Store} this
18948          * @param {Roo.data.Record[]} records The array of Records added
18949          * @param {Number} index The index at which the record(s) were added
18950          */
18951         add : true,
18952         /**
18953          * @event remove
18954          * Fires when a Record has been removed from the Store
18955          * @param {Store} this
18956          * @param {Roo.data.Record} record The Record that was removed
18957          * @param {Number} index The index at which the record was removed
18958          */
18959         remove : true,
18960         /**
18961          * @event update
18962          * Fires when a Record has been updated
18963          * @param {Store} this
18964          * @param {Roo.data.Record} record The Record that was updated
18965          * @param {String} operation The update operation being performed.  Value may be one of:
18966          * <pre><code>
18967  Roo.data.Record.EDIT
18968  Roo.data.Record.REJECT
18969  Roo.data.Record.COMMIT
18970          * </code></pre>
18971          */
18972         update : true,
18973         /**
18974          * @event clear
18975          * Fires when the data cache has been cleared.
18976          * @param {Store} this
18977          */
18978         clear : true,
18979         /**
18980          * @event beforeload
18981          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18982          * the load action will be canceled.
18983          * @param {Store} this
18984          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18985          */
18986         beforeload : true,
18987         /**
18988          * @event load
18989          * Fires after a new set of Records has been loaded.
18990          * @param {Store} this
18991          * @param {Roo.data.Record[]} records The Records that were loaded
18992          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18993          */
18994         load : true,
18995         /**
18996          * @event loadexception
18997          * Fires if an exception occurs in the Proxy during loading.
18998          * Called with the signature of the Proxy's "loadexception" event.
18999          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19000          * 
19001          * @param {Proxy} 
19002          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19003          * @param {Object} load options 
19004          * @param {Object} jsonData from your request (normally this contains the Exception)
19005          */
19006         loadexception : true
19007     });
19008     
19009     if(this.proxy){
19010         this.proxy = Roo.factory(this.proxy, Roo.data);
19011         this.proxy.xmodule = this.xmodule || false;
19012         this.relayEvents(this.proxy,  ["loadexception"]);
19013     }
19014     this.sortToggle = {};
19015
19016     Roo.data.Store.superclass.constructor.call(this);
19017
19018     if(this.inlineData){
19019         this.loadData(this.inlineData);
19020         delete this.inlineData;
19021     }
19022 };
19023 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19024      /**
19025     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19026     * without a remote query - used by combo/forms at present.
19027     */
19028     
19029     /**
19030     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19031     */
19032     /**
19033     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19034     */
19035     /**
19036     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19037     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19038     */
19039     /**
19040     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19041     * on any HTTP request
19042     */
19043     /**
19044     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19045     */
19046     /**
19047     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19048     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19049     */
19050     remoteSort : false,
19051
19052     /**
19053     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19054      * loaded or when a record is removed. (defaults to false).
19055     */
19056     pruneModifiedRecords : false,
19057
19058     // private
19059     lastOptions : null,
19060
19061     /**
19062      * Add Records to the Store and fires the add event.
19063      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19064      */
19065     add : function(records){
19066         records = [].concat(records);
19067         for(var i = 0, len = records.length; i < len; i++){
19068             records[i].join(this);
19069         }
19070         var index = this.data.length;
19071         this.data.addAll(records);
19072         this.fireEvent("add", this, records, index);
19073     },
19074
19075     /**
19076      * Remove a Record from the Store and fires the remove event.
19077      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19078      */
19079     remove : function(record){
19080         var index = this.data.indexOf(record);
19081         this.data.removeAt(index);
19082         if(this.pruneModifiedRecords){
19083             this.modified.remove(record);
19084         }
19085         this.fireEvent("remove", this, record, index);
19086     },
19087
19088     /**
19089      * Remove all Records from the Store and fires the clear event.
19090      */
19091     removeAll : function(){
19092         this.data.clear();
19093         if(this.pruneModifiedRecords){
19094             this.modified = [];
19095         }
19096         this.fireEvent("clear", this);
19097     },
19098
19099     /**
19100      * Inserts Records to the Store at the given index and fires the add event.
19101      * @param {Number} index The start index at which to insert the passed Records.
19102      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19103      */
19104     insert : function(index, records){
19105         records = [].concat(records);
19106         for(var i = 0, len = records.length; i < len; i++){
19107             this.data.insert(index, records[i]);
19108             records[i].join(this);
19109         }
19110         this.fireEvent("add", this, records, index);
19111     },
19112
19113     /**
19114      * Get the index within the cache of the passed Record.
19115      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19116      * @return {Number} The index of the passed Record. Returns -1 if not found.
19117      */
19118     indexOf : function(record){
19119         return this.data.indexOf(record);
19120     },
19121
19122     /**
19123      * Get the index within the cache of the Record with the passed id.
19124      * @param {String} id The id of the Record to find.
19125      * @return {Number} The index of the Record. Returns -1 if not found.
19126      */
19127     indexOfId : function(id){
19128         return this.data.indexOfKey(id);
19129     },
19130
19131     /**
19132      * Get the Record with the specified id.
19133      * @param {String} id The id of the Record to find.
19134      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19135      */
19136     getById : function(id){
19137         return this.data.key(id);
19138     },
19139
19140     /**
19141      * Get the Record at the specified index.
19142      * @param {Number} index The index of the Record to find.
19143      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19144      */
19145     getAt : function(index){
19146         return this.data.itemAt(index);
19147     },
19148
19149     /**
19150      * Returns a range of Records between specified indices.
19151      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19152      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19153      * @return {Roo.data.Record[]} An array of Records
19154      */
19155     getRange : function(start, end){
19156         return this.data.getRange(start, end);
19157     },
19158
19159     // private
19160     storeOptions : function(o){
19161         o = Roo.apply({}, o);
19162         delete o.callback;
19163         delete o.scope;
19164         this.lastOptions = o;
19165     },
19166
19167     /**
19168      * Loads the Record cache from the configured Proxy using the configured Reader.
19169      * <p>
19170      * If using remote paging, then the first load call must specify the <em>start</em>
19171      * and <em>limit</em> properties in the options.params property to establish the initial
19172      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19173      * <p>
19174      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19175      * and this call will return before the new data has been loaded. Perform any post-processing
19176      * in a callback function, or in a "load" event handler.</strong>
19177      * <p>
19178      * @param {Object} options An object containing properties which control loading options:<ul>
19179      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19180      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19181      * passed the following arguments:<ul>
19182      * <li>r : Roo.data.Record[]</li>
19183      * <li>options: Options object from the load call</li>
19184      * <li>success: Boolean success indicator</li></ul></li>
19185      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19186      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19187      * </ul>
19188      */
19189     load : function(options){
19190         options = options || {};
19191         if(this.fireEvent("beforeload", this, options) !== false){
19192             this.storeOptions(options);
19193             var p = Roo.apply(options.params || {}, this.baseParams);
19194             if(this.sortInfo && this.remoteSort){
19195                 var pn = this.paramNames;
19196                 p[pn["sort"]] = this.sortInfo.field;
19197                 p[pn["dir"]] = this.sortInfo.direction;
19198             }
19199             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19200         }
19201     },
19202
19203     /**
19204      * Reloads the Record cache from the configured Proxy using the configured Reader and
19205      * the options from the last load operation performed.
19206      * @param {Object} options (optional) An object containing properties which may override the options
19207      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19208      * the most recently used options are reused).
19209      */
19210     reload : function(options){
19211         this.load(Roo.applyIf(options||{}, this.lastOptions));
19212     },
19213
19214     // private
19215     // Called as a callback by the Reader during a load operation.
19216     loadRecords : function(o, options, success){
19217         if(!o || success === false){
19218             if(success !== false){
19219                 this.fireEvent("load", this, [], options);
19220             }
19221             if(options.callback){
19222                 options.callback.call(options.scope || this, [], options, false);
19223             }
19224             return;
19225         }
19226         // if data returned failure - throw an exception.
19227         if (o.success === false) {
19228             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19229             return;
19230         }
19231         var r = o.records, t = o.totalRecords || r.length;
19232         if(!options || options.add !== true){
19233             if(this.pruneModifiedRecords){
19234                 this.modified = [];
19235             }
19236             for(var i = 0, len = r.length; i < len; i++){
19237                 r[i].join(this);
19238             }
19239             if(this.snapshot){
19240                 this.data = this.snapshot;
19241                 delete this.snapshot;
19242             }
19243             this.data.clear();
19244             this.data.addAll(r);
19245             this.totalLength = t;
19246             this.applySort();
19247             this.fireEvent("datachanged", this);
19248         }else{
19249             this.totalLength = Math.max(t, this.data.length+r.length);
19250             this.add(r);
19251         }
19252         this.fireEvent("load", this, r, options);
19253         if(options.callback){
19254             options.callback.call(options.scope || this, r, options, true);
19255         }
19256     },
19257
19258     /**
19259      * Loads data from a passed data block. A Reader which understands the format of the data
19260      * must have been configured in the constructor.
19261      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19262      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19263      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19264      */
19265     loadData : function(o, append){
19266         var r = this.reader.readRecords(o);
19267         this.loadRecords(r, {add: append}, true);
19268     },
19269
19270     /**
19271      * Gets the number of cached records.
19272      * <p>
19273      * <em>If using paging, this may not be the total size of the dataset. If the data object
19274      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19275      * the data set size</em>
19276      */
19277     getCount : function(){
19278         return this.data.length || 0;
19279     },
19280
19281     /**
19282      * Gets the total number of records in the dataset as returned by the server.
19283      * <p>
19284      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19285      * the dataset size</em>
19286      */
19287     getTotalCount : function(){
19288         return this.totalLength || 0;
19289     },
19290
19291     /**
19292      * Returns the sort state of the Store as an object with two properties:
19293      * <pre><code>
19294  field {String} The name of the field by which the Records are sorted
19295  direction {String} The sort order, "ASC" or "DESC"
19296      * </code></pre>
19297      */
19298     getSortState : function(){
19299         return this.sortInfo;
19300     },
19301
19302     // private
19303     applySort : function(){
19304         if(this.sortInfo && !this.remoteSort){
19305             var s = this.sortInfo, f = s.field;
19306             var st = this.fields.get(f).sortType;
19307             var fn = function(r1, r2){
19308                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19309                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19310             };
19311             this.data.sort(s.direction, fn);
19312             if(this.snapshot && this.snapshot != this.data){
19313                 this.snapshot.sort(s.direction, fn);
19314             }
19315         }
19316     },
19317
19318     /**
19319      * Sets the default sort column and order to be used by the next load operation.
19320      * @param {String} fieldName The name of the field to sort by.
19321      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19322      */
19323     setDefaultSort : function(field, dir){
19324         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19325     },
19326
19327     /**
19328      * Sort the Records.
19329      * If remote sorting is used, the sort is performed on the server, and the cache is
19330      * reloaded. If local sorting is used, the cache is sorted internally.
19331      * @param {String} fieldName The name of the field to sort by.
19332      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19333      */
19334     sort : function(fieldName, dir){
19335         var f = this.fields.get(fieldName);
19336         if(!dir){
19337             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19338                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19339             }else{
19340                 dir = f.sortDir;
19341             }
19342         }
19343         this.sortToggle[f.name] = dir;
19344         this.sortInfo = {field: f.name, direction: dir};
19345         if(!this.remoteSort){
19346             this.applySort();
19347             this.fireEvent("datachanged", this);
19348         }else{
19349             this.load(this.lastOptions);
19350         }
19351     },
19352
19353     /**
19354      * Calls the specified function for each of the Records in the cache.
19355      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19356      * Returning <em>false</em> aborts and exits the iteration.
19357      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19358      */
19359     each : function(fn, scope){
19360         this.data.each(fn, scope);
19361     },
19362
19363     /**
19364      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19365      * (e.g., during paging).
19366      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19367      */
19368     getModifiedRecords : function(){
19369         return this.modified;
19370     },
19371
19372     // private
19373     createFilterFn : function(property, value, anyMatch){
19374         if(!value.exec){ // not a regex
19375             value = String(value);
19376             if(value.length == 0){
19377                 return false;
19378             }
19379             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19380         }
19381         return function(r){
19382             return value.test(r.data[property]);
19383         };
19384     },
19385
19386     /**
19387      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19388      * @param {String} property A field on your records
19389      * @param {Number} start The record index to start at (defaults to 0)
19390      * @param {Number} end The last record index to include (defaults to length - 1)
19391      * @return {Number} The sum
19392      */
19393     sum : function(property, start, end){
19394         var rs = this.data.items, v = 0;
19395         start = start || 0;
19396         end = (end || end === 0) ? end : rs.length-1;
19397
19398         for(var i = start; i <= end; i++){
19399             v += (rs[i].data[property] || 0);
19400         }
19401         return v;
19402     },
19403
19404     /**
19405      * Filter the records by a specified property.
19406      * @param {String} field A field on your records
19407      * @param {String/RegExp} value Either a string that the field
19408      * should start with or a RegExp to test against the field
19409      * @param {Boolean} anyMatch True to match any part not just the beginning
19410      */
19411     filter : function(property, value, anyMatch){
19412         var fn = this.createFilterFn(property, value, anyMatch);
19413         return fn ? this.filterBy(fn) : this.clearFilter();
19414     },
19415
19416     /**
19417      * Filter by a function. The specified function will be called with each
19418      * record in this data source. If the function returns true the record is included,
19419      * otherwise it is filtered.
19420      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19421      * @param {Object} scope (optional) The scope of the function (defaults to this)
19422      */
19423     filterBy : function(fn, scope){
19424         this.snapshot = this.snapshot || this.data;
19425         this.data = this.queryBy(fn, scope||this);
19426         this.fireEvent("datachanged", this);
19427     },
19428
19429     /**
19430      * Query the records by a specified property.
19431      * @param {String} field A field on your records
19432      * @param {String/RegExp} value Either a string that the field
19433      * should start with or a RegExp to test against the field
19434      * @param {Boolean} anyMatch True to match any part not just the beginning
19435      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19436      */
19437     query : function(property, value, anyMatch){
19438         var fn = this.createFilterFn(property, value, anyMatch);
19439         return fn ? this.queryBy(fn) : this.data.clone();
19440     },
19441
19442     /**
19443      * Query by a function. The specified function will be called with each
19444      * record in this data source. If the function returns true the record is included
19445      * in the results.
19446      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19447      * @param {Object} scope (optional) The scope of the function (defaults to this)
19448       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19449      **/
19450     queryBy : function(fn, scope){
19451         var data = this.snapshot || this.data;
19452         return data.filterBy(fn, scope||this);
19453     },
19454
19455     /**
19456      * Collects unique values for a particular dataIndex from this store.
19457      * @param {String} dataIndex The property to collect
19458      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19459      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19460      * @return {Array} An array of the unique values
19461      **/
19462     collect : function(dataIndex, allowNull, bypassFilter){
19463         var d = (bypassFilter === true && this.snapshot) ?
19464                 this.snapshot.items : this.data.items;
19465         var v, sv, r = [], l = {};
19466         for(var i = 0, len = d.length; i < len; i++){
19467             v = d[i].data[dataIndex];
19468             sv = String(v);
19469             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19470                 l[sv] = true;
19471                 r[r.length] = v;
19472             }
19473         }
19474         return r;
19475     },
19476
19477     /**
19478      * Revert to a view of the Record cache with no filtering applied.
19479      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19480      */
19481     clearFilter : function(suppressEvent){
19482         if(this.snapshot && this.snapshot != this.data){
19483             this.data = this.snapshot;
19484             delete this.snapshot;
19485             if(suppressEvent !== true){
19486                 this.fireEvent("datachanged", this);
19487             }
19488         }
19489     },
19490
19491     // private
19492     afterEdit : function(record){
19493         if(this.modified.indexOf(record) == -1){
19494             this.modified.push(record);
19495         }
19496         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19497     },
19498
19499     // private
19500     afterReject : function(record){
19501         this.modified.remove(record);
19502         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19503     },
19504
19505     // private
19506     afterCommit : function(record){
19507         this.modified.remove(record);
19508         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19509     },
19510
19511     /**
19512      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19513      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19514      */
19515     commitChanges : function(){
19516         var m = this.modified.slice(0);
19517         this.modified = [];
19518         for(var i = 0, len = m.length; i < len; i++){
19519             m[i].commit();
19520         }
19521     },
19522
19523     /**
19524      * Cancel outstanding changes on all changed records.
19525      */
19526     rejectChanges : function(){
19527         var m = this.modified.slice(0);
19528         this.modified = [];
19529         for(var i = 0, len = m.length; i < len; i++){
19530             m[i].reject();
19531         }
19532     },
19533
19534     onMetaChange : function(meta, rtype, o){
19535         this.recordType = rtype;
19536         this.fields = rtype.prototype.fields;
19537         delete this.snapshot;
19538         this.sortInfo = meta.sortInfo;
19539         this.modified = [];
19540         this.fireEvent('metachange', this, this.reader.meta);
19541     }
19542 });/*
19543  * Based on:
19544  * Ext JS Library 1.1.1
19545  * Copyright(c) 2006-2007, Ext JS, LLC.
19546  *
19547  * Originally Released Under LGPL - original licence link has changed is not relivant.
19548  *
19549  * Fork - LGPL
19550  * <script type="text/javascript">
19551  */
19552
19553 /**
19554  * @class Roo.data.SimpleStore
19555  * @extends Roo.data.Store
19556  * Small helper class to make creating Stores from Array data easier.
19557  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19558  * @cfg {Array} fields An array of field definition objects, or field name strings.
19559  * @cfg {Array} data The multi-dimensional array of data
19560  * @constructor
19561  * @param {Object} config
19562  */
19563 Roo.data.SimpleStore = function(config){
19564     Roo.data.SimpleStore.superclass.constructor.call(this, {
19565         isLocal : true,
19566         reader: new Roo.data.ArrayReader({
19567                 id: config.id
19568             },
19569             Roo.data.Record.create(config.fields)
19570         ),
19571         proxy : new Roo.data.MemoryProxy(config.data)
19572     });
19573     this.load();
19574 };
19575 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19576  * Based on:
19577  * Ext JS Library 1.1.1
19578  * Copyright(c) 2006-2007, Ext JS, LLC.
19579  *
19580  * Originally Released Under LGPL - original licence link has changed is not relivant.
19581  *
19582  * Fork - LGPL
19583  * <script type="text/javascript">
19584  */
19585
19586 /**
19587 /**
19588  * @extends Roo.data.Store
19589  * @class Roo.data.JsonStore
19590  * Small helper class to make creating Stores for JSON data easier. <br/>
19591 <pre><code>
19592 var store = new Roo.data.JsonStore({
19593     url: 'get-images.php',
19594     root: 'images',
19595     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19596 });
19597 </code></pre>
19598  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19599  * JsonReader and HttpProxy (unless inline data is provided).</b>
19600  * @cfg {Array} fields An array of field definition objects, or field name strings.
19601  * @constructor
19602  * @param {Object} config
19603  */
19604 Roo.data.JsonStore = function(c){
19605     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19606         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19607         reader: new Roo.data.JsonReader(c, c.fields)
19608     }));
19609 };
19610 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19611  * Based on:
19612  * Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  *
19615  * Originally Released Under LGPL - original licence link has changed is not relivant.
19616  *
19617  * Fork - LGPL
19618  * <script type="text/javascript">
19619  */
19620
19621  
19622 Roo.data.Field = function(config){
19623     if(typeof config == "string"){
19624         config = {name: config};
19625     }
19626     Roo.apply(this, config);
19627     
19628     if(!this.type){
19629         this.type = "auto";
19630     }
19631     
19632     var st = Roo.data.SortTypes;
19633     // named sortTypes are supported, here we look them up
19634     if(typeof this.sortType == "string"){
19635         this.sortType = st[this.sortType];
19636     }
19637     
19638     // set default sortType for strings and dates
19639     if(!this.sortType){
19640         switch(this.type){
19641             case "string":
19642                 this.sortType = st.asUCString;
19643                 break;
19644             case "date":
19645                 this.sortType = st.asDate;
19646                 break;
19647             default:
19648                 this.sortType = st.none;
19649         }
19650     }
19651
19652     // define once
19653     var stripRe = /[\$,%]/g;
19654
19655     // prebuilt conversion function for this field, instead of
19656     // switching every time we're reading a value
19657     if(!this.convert){
19658         var cv, dateFormat = this.dateFormat;
19659         switch(this.type){
19660             case "":
19661             case "auto":
19662             case undefined:
19663                 cv = function(v){ return v; };
19664                 break;
19665             case "string":
19666                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19667                 break;
19668             case "int":
19669                 cv = function(v){
19670                     return v !== undefined && v !== null && v !== '' ?
19671                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19672                     };
19673                 break;
19674             case "float":
19675                 cv = function(v){
19676                     return v !== undefined && v !== null && v !== '' ?
19677                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19678                     };
19679                 break;
19680             case "bool":
19681             case "boolean":
19682                 cv = function(v){ return v === true || v === "true" || v == 1; };
19683                 break;
19684             case "date":
19685                 cv = function(v){
19686                     if(!v){
19687                         return '';
19688                     }
19689                     if(v instanceof Date){
19690                         return v;
19691                     }
19692                     if(dateFormat){
19693                         if(dateFormat == "timestamp"){
19694                             return new Date(v*1000);
19695                         }
19696                         return Date.parseDate(v, dateFormat);
19697                     }
19698                     var parsed = Date.parse(v);
19699                     return parsed ? new Date(parsed) : null;
19700                 };
19701              break;
19702             
19703         }
19704         this.convert = cv;
19705     }
19706 };
19707
19708 Roo.data.Field.prototype = {
19709     dateFormat: null,
19710     defaultValue: "",
19711     mapping: null,
19712     sortType : null,
19713     sortDir : "ASC"
19714 };/*
19715  * Based on:
19716  * Ext JS Library 1.1.1
19717  * Copyright(c) 2006-2007, Ext JS, LLC.
19718  *
19719  * Originally Released Under LGPL - original licence link has changed is not relivant.
19720  *
19721  * Fork - LGPL
19722  * <script type="text/javascript">
19723  */
19724  
19725 // Base class for reading structured data from a data source.  This class is intended to be
19726 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19727
19728 /**
19729  * @class Roo.data.DataReader
19730  * Base class for reading structured data from a data source.  This class is intended to be
19731  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19732  */
19733
19734 Roo.data.DataReader = function(meta, recordType){
19735     
19736     this.meta = meta;
19737     
19738     this.recordType = recordType instanceof Array ? 
19739         Roo.data.Record.create(recordType) : recordType;
19740 };
19741
19742 Roo.data.DataReader.prototype = {
19743      /**
19744      * Create an empty record
19745      * @param {Object} data (optional) - overlay some values
19746      * @return {Roo.data.Record} record created.
19747      */
19748     newRow :  function(d) {
19749         var da =  {};
19750         this.recordType.prototype.fields.each(function(c) {
19751             switch( c.type) {
19752                 case 'int' : da[c.name] = 0; break;
19753                 case 'date' : da[c.name] = new Date(); break;
19754                 case 'float' : da[c.name] = 0.0; break;
19755                 case 'boolean' : da[c.name] = false; break;
19756                 default : da[c.name] = ""; break;
19757             }
19758             
19759         });
19760         return new this.recordType(Roo.apply(da, d));
19761     }
19762     
19763 };/*
19764  * Based on:
19765  * Ext JS Library 1.1.1
19766  * Copyright(c) 2006-2007, Ext JS, LLC.
19767  *
19768  * Originally Released Under LGPL - original licence link has changed is not relivant.
19769  *
19770  * Fork - LGPL
19771  * <script type="text/javascript">
19772  */
19773
19774 /**
19775  * @class Roo.data.DataProxy
19776  * @extends Roo.data.Observable
19777  * This class is an abstract base class for implementations which provide retrieval of
19778  * unformatted data objects.<br>
19779  * <p>
19780  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19781  * (of the appropriate type which knows how to parse the data object) to provide a block of
19782  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19783  * <p>
19784  * Custom implementations must implement the load method as described in
19785  * {@link Roo.data.HttpProxy#load}.
19786  */
19787 Roo.data.DataProxy = function(){
19788     this.addEvents({
19789         /**
19790          * @event beforeload
19791          * Fires before a network request is made to retrieve a data object.
19792          * @param {Object} This DataProxy object.
19793          * @param {Object} params The params parameter to the load function.
19794          */
19795         beforeload : true,
19796         /**
19797          * @event load
19798          * Fires before the load method's callback is called.
19799          * @param {Object} This DataProxy object.
19800          * @param {Object} o The data object.
19801          * @param {Object} arg The callback argument object passed to the load function.
19802          */
19803         load : true,
19804         /**
19805          * @event loadexception
19806          * Fires if an Exception occurs during data retrieval.
19807          * @param {Object} This DataProxy object.
19808          * @param {Object} o The data object.
19809          * @param {Object} arg The callback argument object passed to the load function.
19810          * @param {Object} e The Exception.
19811          */
19812         loadexception : true
19813     });
19814     Roo.data.DataProxy.superclass.constructor.call(this);
19815 };
19816
19817 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19818
19819     /**
19820      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19821      */
19822 /*
19823  * Based on:
19824  * Ext JS Library 1.1.1
19825  * Copyright(c) 2006-2007, Ext JS, LLC.
19826  *
19827  * Originally Released Under LGPL - original licence link has changed is not relivant.
19828  *
19829  * Fork - LGPL
19830  * <script type="text/javascript">
19831  */
19832 /**
19833  * @class Roo.data.MemoryProxy
19834  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19835  * to the Reader when its load method is called.
19836  * @constructor
19837  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19838  */
19839 Roo.data.MemoryProxy = function(data){
19840     if (data.data) {
19841         data = data.data;
19842     }
19843     Roo.data.MemoryProxy.superclass.constructor.call(this);
19844     this.data = data;
19845 };
19846
19847 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19848     /**
19849      * Load data from the requested source (in this case an in-memory
19850      * data object passed to the constructor), read the data object into
19851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19852      * process that block using the passed callback.
19853      * @param {Object} params This parameter is not used by the MemoryProxy class.
19854      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19855      * object into a block of Roo.data.Records.
19856      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19857      * The function must be passed <ul>
19858      * <li>The Record block object</li>
19859      * <li>The "arg" argument from the load function</li>
19860      * <li>A boolean success indicator</li>
19861      * </ul>
19862      * @param {Object} scope The scope in which to call the callback
19863      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19864      */
19865     load : function(params, reader, callback, scope, arg){
19866         params = params || {};
19867         var result;
19868         try {
19869             result = reader.readRecords(this.data);
19870         }catch(e){
19871             this.fireEvent("loadexception", this, arg, null, e);
19872             callback.call(scope, null, arg, false);
19873             return;
19874         }
19875         callback.call(scope, result, arg, true);
19876     },
19877     
19878     // private
19879     update : function(params, records){
19880         
19881     }
19882 });/*
19883  * Based on:
19884  * Ext JS Library 1.1.1
19885  * Copyright(c) 2006-2007, Ext JS, LLC.
19886  *
19887  * Originally Released Under LGPL - original licence link has changed is not relivant.
19888  *
19889  * Fork - LGPL
19890  * <script type="text/javascript">
19891  */
19892 /**
19893  * @class Roo.data.HttpProxy
19894  * @extends Roo.data.DataProxy
19895  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19896  * configured to reference a certain URL.<br><br>
19897  * <p>
19898  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19899  * from which the running page was served.<br><br>
19900  * <p>
19901  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19902  * <p>
19903  * Be aware that to enable the browser to parse an XML document, the server must set
19904  * the Content-Type header in the HTTP response to "text/xml".
19905  * @constructor
19906  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19907  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19908  * will be used to make the request.
19909  */
19910 Roo.data.HttpProxy = function(conn){
19911     Roo.data.HttpProxy.superclass.constructor.call(this);
19912     // is conn a conn config or a real conn?
19913     this.conn = conn;
19914     this.useAjax = !conn || !conn.events;
19915   
19916 };
19917
19918 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19919     // thse are take from connection...
19920     
19921     /**
19922      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19923      */
19924     /**
19925      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19926      * extra parameters to each request made by this object. (defaults to undefined)
19927      */
19928     /**
19929      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19930      *  to each request made by this object. (defaults to undefined)
19931      */
19932     /**
19933      * @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)
19934      */
19935     /**
19936      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19937      */
19938      /**
19939      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19940      * @type Boolean
19941      */
19942   
19943
19944     /**
19945      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19946      * @type Boolean
19947      */
19948     /**
19949      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19950      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19951      * a finer-grained basis than the DataProxy events.
19952      */
19953     getConnection : function(){
19954         return this.useAjax ? Roo.Ajax : this.conn;
19955     },
19956
19957     /**
19958      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19959      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19960      * process that block using the passed callback.
19961      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19962      * for the request to the remote server.
19963      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19964      * object into a block of Roo.data.Records.
19965      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19966      * The function must be passed <ul>
19967      * <li>The Record block object</li>
19968      * <li>The "arg" argument from the load function</li>
19969      * <li>A boolean success indicator</li>
19970      * </ul>
19971      * @param {Object} scope The scope in which to call the callback
19972      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19973      */
19974     load : function(params, reader, callback, scope, arg){
19975         if(this.fireEvent("beforeload", this, params) !== false){
19976             var  o = {
19977                 params : params || {},
19978                 request: {
19979                     callback : callback,
19980                     scope : scope,
19981                     arg : arg
19982                 },
19983                 reader: reader,
19984                 callback : this.loadResponse,
19985                 scope: this
19986             };
19987             if(this.useAjax){
19988                 Roo.applyIf(o, this.conn);
19989                 if(this.activeRequest){
19990                     Roo.Ajax.abort(this.activeRequest);
19991                 }
19992                 this.activeRequest = Roo.Ajax.request(o);
19993             }else{
19994                 this.conn.request(o);
19995             }
19996         }else{
19997             callback.call(scope||this, null, arg, false);
19998         }
19999     },
20000
20001     // private
20002     loadResponse : function(o, success, response){
20003         delete this.activeRequest;
20004         if(!success){
20005             this.fireEvent("loadexception", this, o, response);
20006             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20007             return;
20008         }
20009         var result;
20010         try {
20011             result = o.reader.read(response);
20012         }catch(e){
20013             this.fireEvent("loadexception", this, o, response, e);
20014             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20015             return;
20016         }
20017         
20018         this.fireEvent("load", this, o, o.request.arg);
20019         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20020     },
20021
20022     // private
20023     update : function(dataSet){
20024
20025     },
20026
20027     // private
20028     updateResponse : function(dataSet){
20029
20030     }
20031 });/*
20032  * Based on:
20033  * Ext JS Library 1.1.1
20034  * Copyright(c) 2006-2007, Ext JS, LLC.
20035  *
20036  * Originally Released Under LGPL - original licence link has changed is not relivant.
20037  *
20038  * Fork - LGPL
20039  * <script type="text/javascript">
20040  */
20041
20042 /**
20043  * @class Roo.data.ScriptTagProxy
20044  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20045  * other than the originating domain of the running page.<br><br>
20046  * <p>
20047  * <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
20048  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20049  * <p>
20050  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20051  * source code that is used as the source inside a &lt;script> tag.<br><br>
20052  * <p>
20053  * In order for the browser to process the returned data, the server must wrap the data object
20054  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20055  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20056  * depending on whether the callback name was passed:
20057  * <p>
20058  * <pre><code>
20059 boolean scriptTag = false;
20060 String cb = request.getParameter("callback");
20061 if (cb != null) {
20062     scriptTag = true;
20063     response.setContentType("text/javascript");
20064 } else {
20065     response.setContentType("application/x-json");
20066 }
20067 Writer out = response.getWriter();
20068 if (scriptTag) {
20069     out.write(cb + "(");
20070 }
20071 out.print(dataBlock.toJsonString());
20072 if (scriptTag) {
20073     out.write(");");
20074 }
20075 </pre></code>
20076  *
20077  * @constructor
20078  * @param {Object} config A configuration object.
20079  */
20080 Roo.data.ScriptTagProxy = function(config){
20081     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20082     Roo.apply(this, config);
20083     this.head = document.getElementsByTagName("head")[0];
20084 };
20085
20086 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20087
20088 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20089     /**
20090      * @cfg {String} url The URL from which to request the data object.
20091      */
20092     /**
20093      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20094      */
20095     timeout : 30000,
20096     /**
20097      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20098      * the server the name of the callback function set up by the load call to process the returned data object.
20099      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20100      * javascript output which calls this named function passing the data object as its only parameter.
20101      */
20102     callbackParam : "callback",
20103     /**
20104      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20105      * name to the request.
20106      */
20107     nocache : true,
20108
20109     /**
20110      * Load data from the configured URL, read the data object into
20111      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20112      * process that block using the passed callback.
20113      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20114      * for the request to the remote server.
20115      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20116      * object into a block of Roo.data.Records.
20117      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20118      * The function must be passed <ul>
20119      * <li>The Record block object</li>
20120      * <li>The "arg" argument from the load function</li>
20121      * <li>A boolean success indicator</li>
20122      * </ul>
20123      * @param {Object} scope The scope in which to call the callback
20124      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20125      */
20126     load : function(params, reader, callback, scope, arg){
20127         if(this.fireEvent("beforeload", this, params) !== false){
20128
20129             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20130
20131             var url = this.url;
20132             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20133             if(this.nocache){
20134                 url += "&_dc=" + (new Date().getTime());
20135             }
20136             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20137             var trans = {
20138                 id : transId,
20139                 cb : "stcCallback"+transId,
20140                 scriptId : "stcScript"+transId,
20141                 params : params,
20142                 arg : arg,
20143                 url : url,
20144                 callback : callback,
20145                 scope : scope,
20146                 reader : reader
20147             };
20148             var conn = this;
20149
20150             window[trans.cb] = function(o){
20151                 conn.handleResponse(o, trans);
20152             };
20153
20154             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20155
20156             if(this.autoAbort !== false){
20157                 this.abort();
20158             }
20159
20160             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20161
20162             var script = document.createElement("script");
20163             script.setAttribute("src", url);
20164             script.setAttribute("type", "text/javascript");
20165             script.setAttribute("id", trans.scriptId);
20166             this.head.appendChild(script);
20167
20168             this.trans = trans;
20169         }else{
20170             callback.call(scope||this, null, arg, false);
20171         }
20172     },
20173
20174     // private
20175     isLoading : function(){
20176         return this.trans ? true : false;
20177     },
20178
20179     /**
20180      * Abort the current server request.
20181      */
20182     abort : function(){
20183         if(this.isLoading()){
20184             this.destroyTrans(this.trans);
20185         }
20186     },
20187
20188     // private
20189     destroyTrans : function(trans, isLoaded){
20190         this.head.removeChild(document.getElementById(trans.scriptId));
20191         clearTimeout(trans.timeoutId);
20192         if(isLoaded){
20193             window[trans.cb] = undefined;
20194             try{
20195                 delete window[trans.cb];
20196             }catch(e){}
20197         }else{
20198             // if hasn't been loaded, wait for load to remove it to prevent script error
20199             window[trans.cb] = function(){
20200                 window[trans.cb] = undefined;
20201                 try{
20202                     delete window[trans.cb];
20203                 }catch(e){}
20204             };
20205         }
20206     },
20207
20208     // private
20209     handleResponse : function(o, trans){
20210         this.trans = false;
20211         this.destroyTrans(trans, true);
20212         var result;
20213         try {
20214             result = trans.reader.readRecords(o);
20215         }catch(e){
20216             this.fireEvent("loadexception", this, o, trans.arg, e);
20217             trans.callback.call(trans.scope||window, null, trans.arg, false);
20218             return;
20219         }
20220         this.fireEvent("load", this, o, trans.arg);
20221         trans.callback.call(trans.scope||window, result, trans.arg, true);
20222     },
20223
20224     // private
20225     handleFailure : function(trans){
20226         this.trans = false;
20227         this.destroyTrans(trans, false);
20228         this.fireEvent("loadexception", this, null, trans.arg);
20229         trans.callback.call(trans.scope||window, null, trans.arg, false);
20230     }
20231 });/*
20232  * Based on:
20233  * Ext JS Library 1.1.1
20234  * Copyright(c) 2006-2007, Ext JS, LLC.
20235  *
20236  * Originally Released Under LGPL - original licence link has changed is not relivant.
20237  *
20238  * Fork - LGPL
20239  * <script type="text/javascript">
20240  */
20241
20242 /**
20243  * @class Roo.data.JsonReader
20244  * @extends Roo.data.DataReader
20245  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20246  * based on mappings in a provided Roo.data.Record constructor.
20247  * <p>
20248  * Example code:
20249  * <pre><code>
20250 var RecordDef = Roo.data.Record.create([
20251     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20252     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20253 ]);
20254 var myReader = new Roo.data.JsonReader({
20255     totalProperty: "results",    // The property which contains the total dataset size (optional)
20256     root: "rows",                // The property which contains an Array of row objects
20257     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20258 }, RecordDef);
20259 </code></pre>
20260  * <p>
20261  * This would consume a JSON file like this:
20262  * <pre><code>
20263 { 'results': 2, 'rows': [
20264     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20265     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20266 }
20267 </code></pre>
20268  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20269  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20270  * paged from the remote server.
20271  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20272  * @cfg {String} root name of the property which contains the Array of row objects.
20273  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20274  * @constructor
20275  * Create a new JsonReader
20276  * @param {Object} meta Metadata configuration options
20277  * @param {Object} recordType Either an Array of field definition objects,
20278  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20279  */
20280 Roo.data.JsonReader = function(meta, recordType){
20281     
20282     meta = meta || {};
20283     // set some defaults:
20284     Roo.applyIf(meta, {
20285         totalProperty: 'total',
20286         successProperty : 'success',
20287         root : 'data',
20288         id : 'id'
20289     });
20290     
20291     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20292 };
20293 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20294     /**
20295      * This method is only used by a DataProxy which has retrieved data from a remote server.
20296      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20297      * @return {Object} data A data block which is used by an Roo.data.Store object as
20298      * a cache of Roo.data.Records.
20299      */
20300     read : function(response){
20301         var json = response.responseText;
20302         /* eval:var:o */
20303         var o = eval("("+json+")");
20304         if(!o) {
20305             throw {message: "JsonReader.read: Json object not found"};
20306         }
20307         
20308         if(o.metaData){
20309             delete this.ef;
20310             this.meta = o.metaData;
20311             this.recordType = Roo.data.Record.create(o.metaData.fields);
20312             this.onMetaChange(this.meta, this.recordType, o);
20313         }
20314         return this.readRecords(o);
20315     },
20316
20317     // private function a store will implement
20318     onMetaChange : function(meta, recordType, o){
20319
20320     },
20321
20322     /**
20323          * @ignore
20324          */
20325     simpleAccess: function(obj, subsc) {
20326         return obj[subsc];
20327     },
20328
20329         /**
20330          * @ignore
20331          */
20332     getJsonAccessor: function(){
20333         var re = /[\[\.]/;
20334         return function(expr) {
20335             try {
20336                 return(re.test(expr))
20337                     ? new Function("obj", "return obj." + expr)
20338                     : function(obj){
20339                         return obj[expr];
20340                     };
20341             } catch(e){}
20342             return Roo.emptyFn;
20343         };
20344     }(),
20345
20346     /**
20347      * Create a data block containing Roo.data.Records from an XML document.
20348      * @param {Object} o An object which contains an Array of row objects in the property specified
20349      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20350      * which contains the total size of the dataset.
20351      * @return {Object} data A data block which is used by an Roo.data.Store object as
20352      * a cache of Roo.data.Records.
20353      */
20354     readRecords : function(o){
20355         /**
20356          * After any data loads, the raw JSON data is available for further custom processing.
20357          * @type Object
20358          */
20359         this.jsonData = o;
20360         var s = this.meta, Record = this.recordType,
20361             f = Record.prototype.fields, fi = f.items, fl = f.length;
20362
20363 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20364         if (!this.ef) {
20365             if(s.totalProperty) {
20366                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20367                 }
20368                 if(s.successProperty) {
20369                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20370                 }
20371                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20372                 if (s.id) {
20373                         var g = this.getJsonAccessor(s.id);
20374                         this.getId = function(rec) {
20375                                 var r = g(rec);
20376                                 return (r === undefined || r === "") ? null : r;
20377                         };
20378                 } else {
20379                         this.getId = function(){return null;};
20380                 }
20381             this.ef = [];
20382             for(var i = 0; i < fl; i++){
20383                 f = fi[i];
20384                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20385                 this.ef[i] = this.getJsonAccessor(map);
20386             }
20387         }
20388
20389         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20390         if(s.totalProperty){
20391             var v = parseInt(this.getTotal(o), 10);
20392             if(!isNaN(v)){
20393                 totalRecords = v;
20394             }
20395         }
20396         if(s.successProperty){
20397             var v = this.getSuccess(o);
20398             if(v === false || v === 'false'){
20399                 success = false;
20400             }
20401         }
20402         var records = [];
20403             for(var i = 0; i < c; i++){
20404                     var n = root[i];
20405                 var values = {};
20406                 var id = this.getId(n);
20407                 for(var j = 0; j < fl; j++){
20408                     f = fi[j];
20409                 var v = this.ef[j](n);
20410                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20411                 }
20412                 var record = new Record(values, id);
20413                 record.json = n;
20414                 records[i] = record;
20415             }
20416             return {
20417                 success : success,
20418                 records : records,
20419                 totalRecords : totalRecords
20420             };
20421     }
20422 });/*
20423  * Based on:
20424  * Ext JS Library 1.1.1
20425  * Copyright(c) 2006-2007, Ext JS, LLC.
20426  *
20427  * Originally Released Under LGPL - original licence link has changed is not relivant.
20428  *
20429  * Fork - LGPL
20430  * <script type="text/javascript">
20431  */
20432
20433 /**
20434  * @class Roo.data.XmlReader
20435  * @extends Roo.data.DataReader
20436  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20437  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20438  * <p>
20439  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20440  * header in the HTTP response must be set to "text/xml".</em>
20441  * <p>
20442  * Example code:
20443  * <pre><code>
20444 var RecordDef = Roo.data.Record.create([
20445    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20446    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20447 ]);
20448 var myReader = new Roo.data.XmlReader({
20449    totalRecords: "results", // The element which contains the total dataset size (optional)
20450    record: "row",           // The repeated element which contains row information
20451    id: "id"                 // The element within the row that provides an ID for the record (optional)
20452 }, RecordDef);
20453 </code></pre>
20454  * <p>
20455  * This would consume an XML file like this:
20456  * <pre><code>
20457 &lt;?xml?>
20458 &lt;dataset>
20459  &lt;results>2&lt;/results>
20460  &lt;row>
20461    &lt;id>1&lt;/id>
20462    &lt;name>Bill&lt;/name>
20463    &lt;occupation>Gardener&lt;/occupation>
20464  &lt;/row>
20465  &lt;row>
20466    &lt;id>2&lt;/id>
20467    &lt;name>Ben&lt;/name>
20468    &lt;occupation>Horticulturalist&lt;/occupation>
20469  &lt;/row>
20470 &lt;/dataset>
20471 </code></pre>
20472  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20473  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20474  * paged from the remote server.
20475  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20476  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20477  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20478  * a record identifier value.
20479  * @constructor
20480  * Create a new XmlReader
20481  * @param {Object} meta Metadata configuration options
20482  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20483  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20484  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20485  */
20486 Roo.data.XmlReader = function(meta, recordType){
20487     meta = meta || {};
20488     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20489 };
20490 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20491     /**
20492      * This method is only used by a DataProxy which has retrieved data from a remote server.
20493          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20494          * to contain a method called 'responseXML' that returns an XML document object.
20495      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20496      * a cache of Roo.data.Records.
20497      */
20498     read : function(response){
20499         var doc = response.responseXML;
20500         if(!doc) {
20501             throw {message: "XmlReader.read: XML Document not available"};
20502         }
20503         return this.readRecords(doc);
20504     },
20505
20506     /**
20507      * Create a data block containing Roo.data.Records from an XML document.
20508          * @param {Object} doc A parsed XML document.
20509      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20510      * a cache of Roo.data.Records.
20511      */
20512     readRecords : function(doc){
20513         /**
20514          * After any data loads/reads, the raw XML Document is available for further custom processing.
20515          * @type XMLDocument
20516          */
20517         this.xmlData = doc;
20518         var root = doc.documentElement || doc;
20519         var q = Roo.DomQuery;
20520         var recordType = this.recordType, fields = recordType.prototype.fields;
20521         var sid = this.meta.id;
20522         var totalRecords = 0, success = true;
20523         if(this.meta.totalRecords){
20524             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20525         }
20526         
20527         if(this.meta.success){
20528             var sv = q.selectValue(this.meta.success, root, true);
20529             success = sv !== false && sv !== 'false';
20530         }
20531         var records = [];
20532         var ns = q.select(this.meta.record, root);
20533         for(var i = 0, len = ns.length; i < len; i++) {
20534                 var n = ns[i];
20535                 var values = {};
20536                 var id = sid ? q.selectValue(sid, n) : undefined;
20537                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20538                     var f = fields.items[j];
20539                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20540                     v = f.convert(v);
20541                     values[f.name] = v;
20542                 }
20543                 var record = new recordType(values, id);
20544                 record.node = n;
20545                 records[records.length] = record;
20546             }
20547
20548             return {
20549                 success : success,
20550                 records : records,
20551                 totalRecords : totalRecords || records.length
20552             };
20553     }
20554 });/*
20555  * Based on:
20556  * Ext JS Library 1.1.1
20557  * Copyright(c) 2006-2007, Ext JS, LLC.
20558  *
20559  * Originally Released Under LGPL - original licence link has changed is not relivant.
20560  *
20561  * Fork - LGPL
20562  * <script type="text/javascript">
20563  */
20564
20565 /**
20566  * @class Roo.data.ArrayReader
20567  * @extends Roo.data.DataReader
20568  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20569  * Each element of that Array represents a row of data fields. The
20570  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20571  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20572  * <p>
20573  * Example code:.
20574  * <pre><code>
20575 var RecordDef = Roo.data.Record.create([
20576     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20577     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20578 ]);
20579 var myReader = new Roo.data.ArrayReader({
20580     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20581 }, RecordDef);
20582 </code></pre>
20583  * <p>
20584  * This would consume an Array like this:
20585  * <pre><code>
20586 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20587   </code></pre>
20588  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20589  * @constructor
20590  * Create a new JsonReader
20591  * @param {Object} meta Metadata configuration options.
20592  * @param {Object} recordType Either an Array of field definition objects
20593  * as specified to {@link Roo.data.Record#create},
20594  * or an {@link Roo.data.Record} object
20595  * created using {@link Roo.data.Record#create}.
20596  */
20597 Roo.data.ArrayReader = function(meta, recordType){
20598     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20599 };
20600
20601 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20602     /**
20603      * Create a data block containing Roo.data.Records from an XML document.
20604      * @param {Object} o An Array of row objects which represents the dataset.
20605      * @return {Object} data A data block which is used by an Roo.data.Store object as
20606      * a cache of Roo.data.Records.
20607      */
20608     readRecords : function(o){
20609         var sid = this.meta ? this.meta.id : null;
20610         var recordType = this.recordType, fields = recordType.prototype.fields;
20611         var records = [];
20612         var root = o;
20613             for(var i = 0; i < root.length; i++){
20614                     var n = root[i];
20615                 var values = {};
20616                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20617                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20618                 var f = fields.items[j];
20619                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20620                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20621                 v = f.convert(v);
20622                 values[f.name] = v;
20623             }
20624                 var record = new recordType(values, id);
20625                 record.json = n;
20626                 records[records.length] = record;
20627             }
20628             return {
20629                 records : records,
20630                 totalRecords : records.length
20631             };
20632     }
20633 });/*
20634  * Based on:
20635  * Ext JS Library 1.1.1
20636  * Copyright(c) 2006-2007, Ext JS, LLC.
20637  *
20638  * Originally Released Under LGPL - original licence link has changed is not relivant.
20639  *
20640  * Fork - LGPL
20641  * <script type="text/javascript">
20642  */
20643
20644
20645 /**
20646  * @class Roo.data.Tree
20647  * @extends Roo.util.Observable
20648  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20649  * in the tree have most standard DOM functionality.
20650  * @constructor
20651  * @param {Node} root (optional) The root node
20652  */
20653 Roo.data.Tree = function(root){
20654    this.nodeHash = {};
20655    /**
20656     * The root node for this tree
20657     * @type Node
20658     */
20659    this.root = null;
20660    if(root){
20661        this.setRootNode(root);
20662    }
20663    this.addEvents({
20664        /**
20665         * @event append
20666         * Fires when a new child node is appended to a node in this tree.
20667         * @param {Tree} tree The owner tree
20668         * @param {Node} parent The parent node
20669         * @param {Node} node The newly appended node
20670         * @param {Number} index The index of the newly appended node
20671         */
20672        "append" : true,
20673        /**
20674         * @event remove
20675         * Fires when a child node is removed from a node in this tree.
20676         * @param {Tree} tree The owner tree
20677         * @param {Node} parent The parent node
20678         * @param {Node} node The child node removed
20679         */
20680        "remove" : true,
20681        /**
20682         * @event move
20683         * Fires when a node is moved to a new location in the tree
20684         * @param {Tree} tree The owner tree
20685         * @param {Node} node The node moved
20686         * @param {Node} oldParent The old parent of this node
20687         * @param {Node} newParent The new parent of this node
20688         * @param {Number} index The index it was moved to
20689         */
20690        "move" : true,
20691        /**
20692         * @event insert
20693         * Fires when a new child node is inserted in a node in this tree.
20694         * @param {Tree} tree The owner tree
20695         * @param {Node} parent The parent node
20696         * @param {Node} node The child node inserted
20697         * @param {Node} refNode The child node the node was inserted before
20698         */
20699        "insert" : true,
20700        /**
20701         * @event beforeappend
20702         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20703         * @param {Tree} tree The owner tree
20704         * @param {Node} parent The parent node
20705         * @param {Node} node The child node to be appended
20706         */
20707        "beforeappend" : true,
20708        /**
20709         * @event beforeremove
20710         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20711         * @param {Tree} tree The owner tree
20712         * @param {Node} parent The parent node
20713         * @param {Node} node The child node to be removed
20714         */
20715        "beforeremove" : true,
20716        /**
20717         * @event beforemove
20718         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20719         * @param {Tree} tree The owner tree
20720         * @param {Node} node The node being moved
20721         * @param {Node} oldParent The parent of the node
20722         * @param {Node} newParent The new parent the node is moving to
20723         * @param {Number} index The index it is being moved to
20724         */
20725        "beforemove" : true,
20726        /**
20727         * @event beforeinsert
20728         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20729         * @param {Tree} tree The owner tree
20730         * @param {Node} parent The parent node
20731         * @param {Node} node The child node to be inserted
20732         * @param {Node} refNode The child node the node is being inserted before
20733         */
20734        "beforeinsert" : true
20735    });
20736
20737     Roo.data.Tree.superclass.constructor.call(this);
20738 };
20739
20740 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20741     pathSeparator: "/",
20742
20743     proxyNodeEvent : function(){
20744         return this.fireEvent.apply(this, arguments);
20745     },
20746
20747     /**
20748      * Returns the root node for this tree.
20749      * @return {Node}
20750      */
20751     getRootNode : function(){
20752         return this.root;
20753     },
20754
20755     /**
20756      * Sets the root node for this tree.
20757      * @param {Node} node
20758      * @return {Node}
20759      */
20760     setRootNode : function(node){
20761         this.root = node;
20762         node.ownerTree = this;
20763         node.isRoot = true;
20764         this.registerNode(node);
20765         return node;
20766     },
20767
20768     /**
20769      * Gets a node in this tree by its id.
20770      * @param {String} id
20771      * @return {Node}
20772      */
20773     getNodeById : function(id){
20774         return this.nodeHash[id];
20775     },
20776
20777     registerNode : function(node){
20778         this.nodeHash[node.id] = node;
20779     },
20780
20781     unregisterNode : function(node){
20782         delete this.nodeHash[node.id];
20783     },
20784
20785     toString : function(){
20786         return "[Tree"+(this.id?" "+this.id:"")+"]";
20787     }
20788 });
20789
20790 /**
20791  * @class Roo.data.Node
20792  * @extends Roo.util.Observable
20793  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20794  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20795  * @constructor
20796  * @param {Object} attributes The attributes/config for the node
20797  */
20798 Roo.data.Node = function(attributes){
20799     /**
20800      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20801      * @type {Object}
20802      */
20803     this.attributes = attributes || {};
20804     this.leaf = this.attributes.leaf;
20805     /**
20806      * The node id. @type String
20807      */
20808     this.id = this.attributes.id;
20809     if(!this.id){
20810         this.id = Roo.id(null, "ynode-");
20811         this.attributes.id = this.id;
20812     }
20813     /**
20814      * All child nodes of this node. @type Array
20815      */
20816     this.childNodes = [];
20817     if(!this.childNodes.indexOf){ // indexOf is a must
20818         this.childNodes.indexOf = function(o){
20819             for(var i = 0, len = this.length; i < len; i++){
20820                 if(this[i] == o) return i;
20821             }
20822             return -1;
20823         };
20824     }
20825     /**
20826      * The parent node for this node. @type Node
20827      */
20828     this.parentNode = null;
20829     /**
20830      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20831      */
20832     this.firstChild = null;
20833     /**
20834      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20835      */
20836     this.lastChild = null;
20837     /**
20838      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20839      */
20840     this.previousSibling = null;
20841     /**
20842      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20843      */
20844     this.nextSibling = null;
20845
20846     this.addEvents({
20847        /**
20848         * @event append
20849         * Fires when a new child node is appended
20850         * @param {Tree} tree The owner tree
20851         * @param {Node} this This node
20852         * @param {Node} node The newly appended node
20853         * @param {Number} index The index of the newly appended node
20854         */
20855        "append" : true,
20856        /**
20857         * @event remove
20858         * Fires when a child node is removed
20859         * @param {Tree} tree The owner tree
20860         * @param {Node} this This node
20861         * @param {Node} node The removed node
20862         */
20863        "remove" : true,
20864        /**
20865         * @event move
20866         * Fires when this node is moved to a new location in the tree
20867         * @param {Tree} tree The owner tree
20868         * @param {Node} this This node
20869         * @param {Node} oldParent The old parent of this node
20870         * @param {Node} newParent The new parent of this node
20871         * @param {Number} index The index it was moved to
20872         */
20873        "move" : true,
20874        /**
20875         * @event insert
20876         * Fires when a new child node is inserted.
20877         * @param {Tree} tree The owner tree
20878         * @param {Node} this This node
20879         * @param {Node} node The child node inserted
20880         * @param {Node} refNode The child node the node was inserted before
20881         */
20882        "insert" : true,
20883        /**
20884         * @event beforeappend
20885         * Fires before a new child is appended, return false to cancel the append.
20886         * @param {Tree} tree The owner tree
20887         * @param {Node} this This node
20888         * @param {Node} node The child node to be appended
20889         */
20890        "beforeappend" : true,
20891        /**
20892         * @event beforeremove
20893         * Fires before a child is removed, return false to cancel the remove.
20894         * @param {Tree} tree The owner tree
20895         * @param {Node} this This node
20896         * @param {Node} node The child node to be removed
20897         */
20898        "beforeremove" : true,
20899        /**
20900         * @event beforemove
20901         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20902         * @param {Tree} tree The owner tree
20903         * @param {Node} this This node
20904         * @param {Node} oldParent The parent of this node
20905         * @param {Node} newParent The new parent this node is moving to
20906         * @param {Number} index The index it is being moved to
20907         */
20908        "beforemove" : true,
20909        /**
20910         * @event beforeinsert
20911         * Fires before a new child is inserted, return false to cancel the insert.
20912         * @param {Tree} tree The owner tree
20913         * @param {Node} this This node
20914         * @param {Node} node The child node to be inserted
20915         * @param {Node} refNode The child node the node is being inserted before
20916         */
20917        "beforeinsert" : true
20918    });
20919     this.listeners = this.attributes.listeners;
20920     Roo.data.Node.superclass.constructor.call(this);
20921 };
20922
20923 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20924     fireEvent : function(evtName){
20925         // first do standard event for this node
20926         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20927             return false;
20928         }
20929         // then bubble it up to the tree if the event wasn't cancelled
20930         var ot = this.getOwnerTree();
20931         if(ot){
20932             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20933                 return false;
20934             }
20935         }
20936         return true;
20937     },
20938
20939     /**
20940      * Returns true if this node is a leaf
20941      * @return {Boolean}
20942      */
20943     isLeaf : function(){
20944         return this.leaf === true;
20945     },
20946
20947     // private
20948     setFirstChild : function(node){
20949         this.firstChild = node;
20950     },
20951
20952     //private
20953     setLastChild : function(node){
20954         this.lastChild = node;
20955     },
20956
20957
20958     /**
20959      * Returns true if this node is the last child of its parent
20960      * @return {Boolean}
20961      */
20962     isLast : function(){
20963        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20964     },
20965
20966     /**
20967      * Returns true if this node is the first child of its parent
20968      * @return {Boolean}
20969      */
20970     isFirst : function(){
20971        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20972     },
20973
20974     hasChildNodes : function(){
20975         return !this.isLeaf() && this.childNodes.length > 0;
20976     },
20977
20978     /**
20979      * Insert node(s) as the last child node of this node.
20980      * @param {Node/Array} node The node or Array of nodes to append
20981      * @return {Node} The appended node if single append, or null if an array was passed
20982      */
20983     appendChild : function(node){
20984         var multi = false;
20985         if(node instanceof Array){
20986             multi = node;
20987         }else if(arguments.length > 1){
20988             multi = arguments;
20989         }
20990         // if passed an array or multiple args do them one by one
20991         if(multi){
20992             for(var i = 0, len = multi.length; i < len; i++) {
20993                 this.appendChild(multi[i]);
20994             }
20995         }else{
20996             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
20997                 return false;
20998             }
20999             var index = this.childNodes.length;
21000             var oldParent = node.parentNode;
21001             // it's a move, make sure we move it cleanly
21002             if(oldParent){
21003                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21004                     return false;
21005                 }
21006                 oldParent.removeChild(node);
21007             }
21008             index = this.childNodes.length;
21009             if(index == 0){
21010                 this.setFirstChild(node);
21011             }
21012             this.childNodes.push(node);
21013             node.parentNode = this;
21014             var ps = this.childNodes[index-1];
21015             if(ps){
21016                 node.previousSibling = ps;
21017                 ps.nextSibling = node;
21018             }else{
21019                 node.previousSibling = null;
21020             }
21021             node.nextSibling = null;
21022             this.setLastChild(node);
21023             node.setOwnerTree(this.getOwnerTree());
21024             this.fireEvent("append", this.ownerTree, this, node, index);
21025             if(oldParent){
21026                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21027             }
21028             return node;
21029         }
21030     },
21031
21032     /**
21033      * Removes a child node from this node.
21034      * @param {Node} node The node to remove
21035      * @return {Node} The removed node
21036      */
21037     removeChild : function(node){
21038         var index = this.childNodes.indexOf(node);
21039         if(index == -1){
21040             return false;
21041         }
21042         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21043             return false;
21044         }
21045
21046         // remove it from childNodes collection
21047         this.childNodes.splice(index, 1);
21048
21049         // update siblings
21050         if(node.previousSibling){
21051             node.previousSibling.nextSibling = node.nextSibling;
21052         }
21053         if(node.nextSibling){
21054             node.nextSibling.previousSibling = node.previousSibling;
21055         }
21056
21057         // update child refs
21058         if(this.firstChild == node){
21059             this.setFirstChild(node.nextSibling);
21060         }
21061         if(this.lastChild == node){
21062             this.setLastChild(node.previousSibling);
21063         }
21064
21065         node.setOwnerTree(null);
21066         // clear any references from the node
21067         node.parentNode = null;
21068         node.previousSibling = null;
21069         node.nextSibling = null;
21070         this.fireEvent("remove", this.ownerTree, this, node);
21071         return node;
21072     },
21073
21074     /**
21075      * Inserts the first node before the second node in this nodes childNodes collection.
21076      * @param {Node} node The node to insert
21077      * @param {Node} refNode The node to insert before (if null the node is appended)
21078      * @return {Node} The inserted node
21079      */
21080     insertBefore : function(node, refNode){
21081         if(!refNode){ // like standard Dom, refNode can be null for append
21082             return this.appendChild(node);
21083         }
21084         // nothing to do
21085         if(node == refNode){
21086             return false;
21087         }
21088
21089         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21090             return false;
21091         }
21092         var index = this.childNodes.indexOf(refNode);
21093         var oldParent = node.parentNode;
21094         var refIndex = index;
21095
21096         // when moving internally, indexes will change after remove
21097         if(oldParent == this && this.childNodes.indexOf(node) < index){
21098             refIndex--;
21099         }
21100
21101         // it's a move, make sure we move it cleanly
21102         if(oldParent){
21103             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21104                 return false;
21105             }
21106             oldParent.removeChild(node);
21107         }
21108         if(refIndex == 0){
21109             this.setFirstChild(node);
21110         }
21111         this.childNodes.splice(refIndex, 0, node);
21112         node.parentNode = this;
21113         var ps = this.childNodes[refIndex-1];
21114         if(ps){
21115             node.previousSibling = ps;
21116             ps.nextSibling = node;
21117         }else{
21118             node.previousSibling = null;
21119         }
21120         node.nextSibling = refNode;
21121         refNode.previousSibling = node;
21122         node.setOwnerTree(this.getOwnerTree());
21123         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21124         if(oldParent){
21125             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21126         }
21127         return node;
21128     },
21129
21130     /**
21131      * Returns the child node at the specified index.
21132      * @param {Number} index
21133      * @return {Node}
21134      */
21135     item : function(index){
21136         return this.childNodes[index];
21137     },
21138
21139     /**
21140      * Replaces one child node in this node with another.
21141      * @param {Node} newChild The replacement node
21142      * @param {Node} oldChild The node to replace
21143      * @return {Node} The replaced node
21144      */
21145     replaceChild : function(newChild, oldChild){
21146         this.insertBefore(newChild, oldChild);
21147         this.removeChild(oldChild);
21148         return oldChild;
21149     },
21150
21151     /**
21152      * Returns the index of a child node
21153      * @param {Node} node
21154      * @return {Number} The index of the node or -1 if it was not found
21155      */
21156     indexOf : function(child){
21157         return this.childNodes.indexOf(child);
21158     },
21159
21160     /**
21161      * Returns the tree this node is in.
21162      * @return {Tree}
21163      */
21164     getOwnerTree : function(){
21165         // if it doesn't have one, look for one
21166         if(!this.ownerTree){
21167             var p = this;
21168             while(p){
21169                 if(p.ownerTree){
21170                     this.ownerTree = p.ownerTree;
21171                     break;
21172                 }
21173                 p = p.parentNode;
21174             }
21175         }
21176         return this.ownerTree;
21177     },
21178
21179     /**
21180      * Returns depth of this node (the root node has a depth of 0)
21181      * @return {Number}
21182      */
21183     getDepth : function(){
21184         var depth = 0;
21185         var p = this;
21186         while(p.parentNode){
21187             ++depth;
21188             p = p.parentNode;
21189         }
21190         return depth;
21191     },
21192
21193     // private
21194     setOwnerTree : function(tree){
21195         // if it's move, we need to update everyone
21196         if(tree != this.ownerTree){
21197             if(this.ownerTree){
21198                 this.ownerTree.unregisterNode(this);
21199             }
21200             this.ownerTree = tree;
21201             var cs = this.childNodes;
21202             for(var i = 0, len = cs.length; i < len; i++) {
21203                 cs[i].setOwnerTree(tree);
21204             }
21205             if(tree){
21206                 tree.registerNode(this);
21207             }
21208         }
21209     },
21210
21211     /**
21212      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21213      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21214      * @return {String} The path
21215      */
21216     getPath : function(attr){
21217         attr = attr || "id";
21218         var p = this.parentNode;
21219         var b = [this.attributes[attr]];
21220         while(p){
21221             b.unshift(p.attributes[attr]);
21222             p = p.parentNode;
21223         }
21224         var sep = this.getOwnerTree().pathSeparator;
21225         return sep + b.join(sep);
21226     },
21227
21228     /**
21229      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21230      * function call will be the scope provided or the current node. The arguments to the function
21231      * will be the args provided or the current node. If the function returns false at any point,
21232      * the bubble is stopped.
21233      * @param {Function} fn The function to call
21234      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21235      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21236      */
21237     bubble : function(fn, scope, args){
21238         var p = this;
21239         while(p){
21240             if(fn.call(scope || p, args || p) === false){
21241                 break;
21242             }
21243             p = p.parentNode;
21244         }
21245     },
21246
21247     /**
21248      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21249      * function call will be the scope provided or the current node. The arguments to the function
21250      * will be the args provided or the current node. If the function returns false at any point,
21251      * the cascade is stopped on that branch.
21252      * @param {Function} fn The function to call
21253      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21254      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21255      */
21256     cascade : function(fn, scope, args){
21257         if(fn.call(scope || this, args || this) !== false){
21258             var cs = this.childNodes;
21259             for(var i = 0, len = cs.length; i < len; i++) {
21260                 cs[i].cascade(fn, scope, args);
21261             }
21262         }
21263     },
21264
21265     /**
21266      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21267      * function call will be the scope provided or the current node. The arguments to the function
21268      * will be the args provided or the current node. If the function returns false at any point,
21269      * the iteration stops.
21270      * @param {Function} fn The function to call
21271      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21272      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21273      */
21274     eachChild : function(fn, scope, args){
21275         var cs = this.childNodes;
21276         for(var i = 0, len = cs.length; i < len; i++) {
21277                 if(fn.call(scope || this, args || cs[i]) === false){
21278                     break;
21279                 }
21280         }
21281     },
21282
21283     /**
21284      * Finds the first child that has the attribute with the specified value.
21285      * @param {String} attribute The attribute name
21286      * @param {Mixed} value The value to search for
21287      * @return {Node} The found child or null if none was found
21288      */
21289     findChild : function(attribute, value){
21290         var cs = this.childNodes;
21291         for(var i = 0, len = cs.length; i < len; i++) {
21292                 if(cs[i].attributes[attribute] == value){
21293                     return cs[i];
21294                 }
21295         }
21296         return null;
21297     },
21298
21299     /**
21300      * Finds the first child by a custom function. The child matches if the function passed
21301      * returns true.
21302      * @param {Function} fn
21303      * @param {Object} scope (optional)
21304      * @return {Node} The found child or null if none was found
21305      */
21306     findChildBy : function(fn, scope){
21307         var cs = this.childNodes;
21308         for(var i = 0, len = cs.length; i < len; i++) {
21309                 if(fn.call(scope||cs[i], cs[i]) === true){
21310                     return cs[i];
21311                 }
21312         }
21313         return null;
21314     },
21315
21316     /**
21317      * Sorts this nodes children using the supplied sort function
21318      * @param {Function} fn
21319      * @param {Object} scope (optional)
21320      */
21321     sort : function(fn, scope){
21322         var cs = this.childNodes;
21323         var len = cs.length;
21324         if(len > 0){
21325             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21326             cs.sort(sortFn);
21327             for(var i = 0; i < len; i++){
21328                 var n = cs[i];
21329                 n.previousSibling = cs[i-1];
21330                 n.nextSibling = cs[i+1];
21331                 if(i == 0){
21332                     this.setFirstChild(n);
21333                 }
21334                 if(i == len-1){
21335                     this.setLastChild(n);
21336                 }
21337             }
21338         }
21339     },
21340
21341     /**
21342      * Returns true if this node is an ancestor (at any point) of the passed node.
21343      * @param {Node} node
21344      * @return {Boolean}
21345      */
21346     contains : function(node){
21347         return node.isAncestor(this);
21348     },
21349
21350     /**
21351      * Returns true if the passed node is an ancestor (at any point) of this node.
21352      * @param {Node} node
21353      * @return {Boolean}
21354      */
21355     isAncestor : function(node){
21356         var p = this.parentNode;
21357         while(p){
21358             if(p == node){
21359                 return true;
21360             }
21361             p = p.parentNode;
21362         }
21363         return false;
21364     },
21365
21366     toString : function(){
21367         return "[Node"+(this.id?" "+this.id:"")+"]";
21368     }
21369 });/*
21370  * Based on:
21371  * Ext JS Library 1.1.1
21372  * Copyright(c) 2006-2007, Ext JS, LLC.
21373  *
21374  * Originally Released Under LGPL - original licence link has changed is not relivant.
21375  *
21376  * Fork - LGPL
21377  * <script type="text/javascript">
21378  */
21379  
21380
21381 /**
21382  * @class Roo.ComponentMgr
21383  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21384  * @singleton
21385  */
21386 Roo.ComponentMgr = function(){
21387     var all = new Roo.util.MixedCollection();
21388
21389     return {
21390         /**
21391          * Registers a component.
21392          * @param {Roo.Component} c The component
21393          */
21394         register : function(c){
21395             all.add(c);
21396         },
21397
21398         /**
21399          * Unregisters a component.
21400          * @param {Roo.Component} c The component
21401          */
21402         unregister : function(c){
21403             all.remove(c);
21404         },
21405
21406         /**
21407          * Returns a component by id
21408          * @param {String} id The component id
21409          */
21410         get : function(id){
21411             return all.get(id);
21412         },
21413
21414         /**
21415          * Registers a function that will be called when a specified component is added to ComponentMgr
21416          * @param {String} id The component id
21417          * @param {Funtction} fn The callback function
21418          * @param {Object} scope The scope of the callback
21419          */
21420         onAvailable : function(id, fn, scope){
21421             all.on("add", function(index, o){
21422                 if(o.id == id){
21423                     fn.call(scope || o, o);
21424                     all.un("add", fn, scope);
21425                 }
21426             });
21427         }
21428     };
21429 }();/*
21430  * Based on:
21431  * Ext JS Library 1.1.1
21432  * Copyright(c) 2006-2007, Ext JS, LLC.
21433  *
21434  * Originally Released Under LGPL - original licence link has changed is not relivant.
21435  *
21436  * Fork - LGPL
21437  * <script type="text/javascript">
21438  */
21439  
21440 /**
21441  * @class Roo.Component
21442  * @extends Roo.util.Observable
21443  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21444  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21445  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21446  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21447  * All visual components (widgets) that require rendering into a layout should subclass Component.
21448  * @constructor
21449  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21450  * 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
21451  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21452  */
21453 Roo.Component = function(config){
21454     config = config || {};
21455     if(config.tagName || config.dom || typeof config == "string"){ // element object
21456         config = {el: config, id: config.id || config};
21457     }
21458     this.initialConfig = config;
21459
21460     Roo.apply(this, config);
21461     this.addEvents({
21462         /**
21463          * @event disable
21464          * Fires after the component is disabled.
21465              * @param {Roo.Component} this
21466              */
21467         disable : true,
21468         /**
21469          * @event enable
21470          * Fires after the component is enabled.
21471              * @param {Roo.Component} this
21472              */
21473         enable : true,
21474         /**
21475          * @event beforeshow
21476          * Fires before the component is shown.  Return false to stop the show.
21477              * @param {Roo.Component} this
21478              */
21479         beforeshow : true,
21480         /**
21481          * @event show
21482          * Fires after the component is shown.
21483              * @param {Roo.Component} this
21484              */
21485         show : true,
21486         /**
21487          * @event beforehide
21488          * Fires before the component is hidden. Return false to stop the hide.
21489              * @param {Roo.Component} this
21490              */
21491         beforehide : true,
21492         /**
21493          * @event hide
21494          * Fires after the component is hidden.
21495              * @param {Roo.Component} this
21496              */
21497         hide : true,
21498         /**
21499          * @event beforerender
21500          * Fires before the component is rendered. Return false to stop the render.
21501              * @param {Roo.Component} this
21502              */
21503         beforerender : true,
21504         /**
21505          * @event render
21506          * Fires after the component is rendered.
21507              * @param {Roo.Component} this
21508              */
21509         render : true,
21510         /**
21511          * @event beforedestroy
21512          * Fires before the component is destroyed. Return false to stop the destroy.
21513              * @param {Roo.Component} this
21514              */
21515         beforedestroy : true,
21516         /**
21517          * @event destroy
21518          * Fires after the component is destroyed.
21519              * @param {Roo.Component} this
21520              */
21521         destroy : true
21522     });
21523     if(!this.id){
21524         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21525     }
21526     Roo.ComponentMgr.register(this);
21527     Roo.Component.superclass.constructor.call(this);
21528     this.initComponent();
21529     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21530         this.render(this.renderTo);
21531         delete this.renderTo;
21532     }
21533 };
21534
21535 // private
21536 Roo.Component.AUTO_ID = 1000;
21537
21538 Roo.extend(Roo.Component, Roo.util.Observable, {
21539     /**
21540      * @property {Boolean} hidden
21541      * true if this component is hidden. Read-only.
21542      */
21543     hidden : false,
21544     /**
21545      * true if this component is disabled. Read-only.
21546      */
21547     disabled : false,
21548     /**
21549      * true if this component has been rendered. Read-only.
21550      */
21551     rendered : false,
21552     
21553     /** @cfg {String} disableClass
21554      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21555      */
21556     disabledClass : "x-item-disabled",
21557         /** @cfg {Boolean} allowDomMove
21558          * Whether the component can move the Dom node when rendering (defaults to true).
21559          */
21560     allowDomMove : true,
21561     /** @cfg {String} hideMode
21562      * How this component should hidden. Supported values are
21563      * "visibility" (css visibility), "offsets" (negative offset position) and
21564      * "display" (css display) - defaults to "display".
21565      */
21566     hideMode: 'display',
21567
21568     // private
21569     ctype : "Roo.Component",
21570
21571     /** @cfg {String} actionMode 
21572      * which property holds the element that used for  hide() / show() / disable() / enable()
21573      * default is 'el' 
21574      */
21575     actionMode : "el",
21576
21577     // private
21578     getActionEl : function(){
21579         return this[this.actionMode];
21580     },
21581
21582     initComponent : Roo.emptyFn,
21583     /**
21584      * If this is a lazy rendering component, render it to its container element.
21585      * @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.
21586      */
21587     render : function(container, position){
21588         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21589             if(!container && this.el){
21590                 this.el = Roo.get(this.el);
21591                 container = this.el.dom.parentNode;
21592                 this.allowDomMove = false;
21593             }
21594             this.container = Roo.get(container);
21595             this.rendered = true;
21596             if(position !== undefined){
21597                 if(typeof position == 'number'){
21598                     position = this.container.dom.childNodes[position];
21599                 }else{
21600                     position = Roo.getDom(position);
21601                 }
21602             }
21603             this.onRender(this.container, position || null);
21604             if(this.cls){
21605                 this.el.addClass(this.cls);
21606                 delete this.cls;
21607             }
21608             if(this.style){
21609                 this.el.applyStyles(this.style);
21610                 delete this.style;
21611             }
21612             this.fireEvent("render", this);
21613             this.afterRender(this.container);
21614             if(this.hidden){
21615                 this.hide();
21616             }
21617             if(this.disabled){
21618                 this.disable();
21619             }
21620         }
21621         return this;
21622     },
21623
21624     // private
21625     // default function is not really useful
21626     onRender : function(ct, position){
21627         if(this.el){
21628             this.el = Roo.get(this.el);
21629             if(this.allowDomMove !== false){
21630                 ct.dom.insertBefore(this.el.dom, position);
21631             }
21632         }
21633     },
21634
21635     // private
21636     getAutoCreate : function(){
21637         var cfg = typeof this.autoCreate == "object" ?
21638                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21639         if(this.id && !cfg.id){
21640             cfg.id = this.id;
21641         }
21642         return cfg;
21643     },
21644
21645     // private
21646     afterRender : Roo.emptyFn,
21647
21648     /**
21649      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21650      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21651      */
21652     destroy : function(){
21653         if(this.fireEvent("beforedestroy", this) !== false){
21654             this.purgeListeners();
21655             this.beforeDestroy();
21656             if(this.rendered){
21657                 this.el.removeAllListeners();
21658                 this.el.remove();
21659                 if(this.actionMode == "container"){
21660                     this.container.remove();
21661                 }
21662             }
21663             this.onDestroy();
21664             Roo.ComponentMgr.unregister(this);
21665             this.fireEvent("destroy", this);
21666         }
21667     },
21668
21669         // private
21670     beforeDestroy : function(){
21671
21672     },
21673
21674         // private
21675         onDestroy : function(){
21676
21677     },
21678
21679     /**
21680      * Returns the underlying {@link Roo.Element}.
21681      * @return {Roo.Element} The element
21682      */
21683     getEl : function(){
21684         return this.el;
21685     },
21686
21687     /**
21688      * Returns the id of this component.
21689      * @return {String}
21690      */
21691     getId : function(){
21692         return this.id;
21693     },
21694
21695     /**
21696      * Try to focus this component.
21697      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21698      * @return {Roo.Component} this
21699      */
21700     focus : function(selectText){
21701         if(this.rendered){
21702             this.el.focus();
21703             if(selectText === true){
21704                 this.el.dom.select();
21705             }
21706         }
21707         return this;
21708     },
21709
21710     // private
21711     blur : function(){
21712         if(this.rendered){
21713             this.el.blur();
21714         }
21715         return this;
21716     },
21717
21718     /**
21719      * Disable this component.
21720      * @return {Roo.Component} this
21721      */
21722     disable : function(){
21723         if(this.rendered){
21724             this.onDisable();
21725         }
21726         this.disabled = true;
21727         this.fireEvent("disable", this);
21728         return this;
21729     },
21730
21731         // private
21732     onDisable : function(){
21733         this.getActionEl().addClass(this.disabledClass);
21734         this.el.dom.disabled = true;
21735     },
21736
21737     /**
21738      * Enable this component.
21739      * @return {Roo.Component} this
21740      */
21741     enable : function(){
21742         if(this.rendered){
21743             this.onEnable();
21744         }
21745         this.disabled = false;
21746         this.fireEvent("enable", this);
21747         return this;
21748     },
21749
21750         // private
21751     onEnable : function(){
21752         this.getActionEl().removeClass(this.disabledClass);
21753         this.el.dom.disabled = false;
21754     },
21755
21756     /**
21757      * Convenience function for setting disabled/enabled by boolean.
21758      * @param {Boolean} disabled
21759      */
21760     setDisabled : function(disabled){
21761         this[disabled ? "disable" : "enable"]();
21762     },
21763
21764     /**
21765      * Show this component.
21766      * @return {Roo.Component} this
21767      */
21768     show: function(){
21769         if(this.fireEvent("beforeshow", this) !== false){
21770             this.hidden = false;
21771             if(this.rendered){
21772                 this.onShow();
21773             }
21774             this.fireEvent("show", this);
21775         }
21776         return this;
21777     },
21778
21779     // private
21780     onShow : function(){
21781         var ae = this.getActionEl();
21782         if(this.hideMode == 'visibility'){
21783             ae.dom.style.visibility = "visible";
21784         }else if(this.hideMode == 'offsets'){
21785             ae.removeClass('x-hidden');
21786         }else{
21787             ae.dom.style.display = "";
21788         }
21789     },
21790
21791     /**
21792      * Hide this component.
21793      * @return {Roo.Component} this
21794      */
21795     hide: function(){
21796         if(this.fireEvent("beforehide", this) !== false){
21797             this.hidden = true;
21798             if(this.rendered){
21799                 this.onHide();
21800             }
21801             this.fireEvent("hide", this);
21802         }
21803         return this;
21804     },
21805
21806     // private
21807     onHide : function(){
21808         var ae = this.getActionEl();
21809         if(this.hideMode == 'visibility'){
21810             ae.dom.style.visibility = "hidden";
21811         }else if(this.hideMode == 'offsets'){
21812             ae.addClass('x-hidden');
21813         }else{
21814             ae.dom.style.display = "none";
21815         }
21816     },
21817
21818     /**
21819      * Convenience function to hide or show this component by boolean.
21820      * @param {Boolean} visible True to show, false to hide
21821      * @return {Roo.Component} this
21822      */
21823     setVisible: function(visible){
21824         if(visible) {
21825             this.show();
21826         }else{
21827             this.hide();
21828         }
21829         return this;
21830     },
21831
21832     /**
21833      * Returns true if this component is visible.
21834      */
21835     isVisible : function(){
21836         return this.getActionEl().isVisible();
21837     },
21838
21839     cloneConfig : function(overrides){
21840         overrides = overrides || {};
21841         var id = overrides.id || Roo.id();
21842         var cfg = Roo.applyIf(overrides, this.initialConfig);
21843         cfg.id = id; // prevent dup id
21844         return new this.constructor(cfg);
21845     }
21846 });/*
21847  * Based on:
21848  * Ext JS Library 1.1.1
21849  * Copyright(c) 2006-2007, Ext JS, LLC.
21850  *
21851  * Originally Released Under LGPL - original licence link has changed is not relivant.
21852  *
21853  * Fork - LGPL
21854  * <script type="text/javascript">
21855  */
21856  (function(){ 
21857 /**
21858  * @class Roo.Layer
21859  * @extends Roo.Element
21860  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21861  * automatic maintaining of shadow/shim positions.
21862  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21863  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21864  * you can pass a string with a CSS class name. False turns off the shadow.
21865  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21866  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21867  * @cfg {String} cls CSS class to add to the element
21868  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21869  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21870  * @constructor
21871  * @param {Object} config An object with config options.
21872  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21873  */
21874
21875 Roo.Layer = function(config, existingEl){
21876     config = config || {};
21877     var dh = Roo.DomHelper;
21878     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21879     if(existingEl){
21880         this.dom = Roo.getDom(existingEl);
21881     }
21882     if(!this.dom){
21883         var o = config.dh || {tag: "div", cls: "x-layer"};
21884         this.dom = dh.append(pel, o);
21885     }
21886     if(config.cls){
21887         this.addClass(config.cls);
21888     }
21889     this.constrain = config.constrain !== false;
21890     this.visibilityMode = Roo.Element.VISIBILITY;
21891     if(config.id){
21892         this.id = this.dom.id = config.id;
21893     }else{
21894         this.id = Roo.id(this.dom);
21895     }
21896     this.zindex = config.zindex || this.getZIndex();
21897     this.position("absolute", this.zindex);
21898     if(config.shadow){
21899         this.shadowOffset = config.shadowOffset || 4;
21900         this.shadow = new Roo.Shadow({
21901             offset : this.shadowOffset,
21902             mode : config.shadow
21903         });
21904     }else{
21905         this.shadowOffset = 0;
21906     }
21907     this.useShim = config.shim !== false && Roo.useShims;
21908     this.useDisplay = config.useDisplay;
21909     this.hide();
21910 };
21911
21912 var supr = Roo.Element.prototype;
21913
21914 // shims are shared among layer to keep from having 100 iframes
21915 var shims = [];
21916
21917 Roo.extend(Roo.Layer, Roo.Element, {
21918
21919     getZIndex : function(){
21920         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21921     },
21922
21923     getShim : function(){
21924         if(!this.useShim){
21925             return null;
21926         }
21927         if(this.shim){
21928             return this.shim;
21929         }
21930         var shim = shims.shift();
21931         if(!shim){
21932             shim = this.createShim();
21933             shim.enableDisplayMode('block');
21934             shim.dom.style.display = 'none';
21935             shim.dom.style.visibility = 'visible';
21936         }
21937         var pn = this.dom.parentNode;
21938         if(shim.dom.parentNode != pn){
21939             pn.insertBefore(shim.dom, this.dom);
21940         }
21941         shim.setStyle('z-index', this.getZIndex()-2);
21942         this.shim = shim;
21943         return shim;
21944     },
21945
21946     hideShim : function(){
21947         if(this.shim){
21948             this.shim.setDisplayed(false);
21949             shims.push(this.shim);
21950             delete this.shim;
21951         }
21952     },
21953
21954     disableShadow : function(){
21955         if(this.shadow){
21956             this.shadowDisabled = true;
21957             this.shadow.hide();
21958             this.lastShadowOffset = this.shadowOffset;
21959             this.shadowOffset = 0;
21960         }
21961     },
21962
21963     enableShadow : function(show){
21964         if(this.shadow){
21965             this.shadowDisabled = false;
21966             this.shadowOffset = this.lastShadowOffset;
21967             delete this.lastShadowOffset;
21968             if(show){
21969                 this.sync(true);
21970             }
21971         }
21972     },
21973
21974     // private
21975     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21976     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21977     sync : function(doShow){
21978         var sw = this.shadow;
21979         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21980             var sh = this.getShim();
21981
21982             var w = this.getWidth(),
21983                 h = this.getHeight();
21984
21985             var l = this.getLeft(true),
21986                 t = this.getTop(true);
21987
21988             if(sw && !this.shadowDisabled){
21989                 if(doShow && !sw.isVisible()){
21990                     sw.show(this);
21991                 }else{
21992                     sw.realign(l, t, w, h);
21993                 }
21994                 if(sh){
21995                     if(doShow){
21996                        sh.show();
21997                     }
21998                     // fit the shim behind the shadow, so it is shimmed too
21999                     var a = sw.adjusts, s = sh.dom.style;
22000                     s.left = (Math.min(l, l+a.l))+"px";
22001                     s.top = (Math.min(t, t+a.t))+"px";
22002                     s.width = (w+a.w)+"px";
22003                     s.height = (h+a.h)+"px";
22004                 }
22005             }else if(sh){
22006                 if(doShow){
22007                    sh.show();
22008                 }
22009                 sh.setSize(w, h);
22010                 sh.setLeftTop(l, t);
22011             }
22012             
22013         }
22014     },
22015
22016     // private
22017     destroy : function(){
22018         this.hideShim();
22019         if(this.shadow){
22020             this.shadow.hide();
22021         }
22022         this.removeAllListeners();
22023         var pn = this.dom.parentNode;
22024         if(pn){
22025             pn.removeChild(this.dom);
22026         }
22027         Roo.Element.uncache(this.id);
22028     },
22029
22030     remove : function(){
22031         this.destroy();
22032     },
22033
22034     // private
22035     beginUpdate : function(){
22036         this.updating = true;
22037     },
22038
22039     // private
22040     endUpdate : function(){
22041         this.updating = false;
22042         this.sync(true);
22043     },
22044
22045     // private
22046     hideUnders : function(negOffset){
22047         if(this.shadow){
22048             this.shadow.hide();
22049         }
22050         this.hideShim();
22051     },
22052
22053     // private
22054     constrainXY : function(){
22055         if(this.constrain){
22056             var vw = Roo.lib.Dom.getViewWidth(),
22057                 vh = Roo.lib.Dom.getViewHeight();
22058             var s = Roo.get(document).getScroll();
22059
22060             var xy = this.getXY();
22061             var x = xy[0], y = xy[1];   
22062             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22063             // only move it if it needs it
22064             var moved = false;
22065             // first validate right/bottom
22066             if((x + w) > vw+s.left){
22067                 x = vw - w - this.shadowOffset;
22068                 moved = true;
22069             }
22070             if((y + h) > vh+s.top){
22071                 y = vh - h - this.shadowOffset;
22072                 moved = true;
22073             }
22074             // then make sure top/left isn't negative
22075             if(x < s.left){
22076                 x = s.left;
22077                 moved = true;
22078             }
22079             if(y < s.top){
22080                 y = s.top;
22081                 moved = true;
22082             }
22083             if(moved){
22084                 if(this.avoidY){
22085                     var ay = this.avoidY;
22086                     if(y <= ay && (y+h) >= ay){
22087                         y = ay-h-5;   
22088                     }
22089                 }
22090                 xy = [x, y];
22091                 this.storeXY(xy);
22092                 supr.setXY.call(this, xy);
22093                 this.sync();
22094             }
22095         }
22096     },
22097
22098     isVisible : function(){
22099         return this.visible;    
22100     },
22101
22102     // private
22103     showAction : function(){
22104         this.visible = true; // track visibility to prevent getStyle calls
22105         if(this.useDisplay === true){
22106             this.setDisplayed("");
22107         }else if(this.lastXY){
22108             supr.setXY.call(this, this.lastXY);
22109         }else if(this.lastLT){
22110             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22111         }
22112     },
22113
22114     // private
22115     hideAction : function(){
22116         this.visible = false;
22117         if(this.useDisplay === true){
22118             this.setDisplayed(false);
22119         }else{
22120             this.setLeftTop(-10000,-10000);
22121         }
22122     },
22123
22124     // overridden Element method
22125     setVisible : function(v, a, d, c, e){
22126         if(v){
22127             this.showAction();
22128         }
22129         if(a && v){
22130             var cb = function(){
22131                 this.sync(true);
22132                 if(c){
22133                     c();
22134                 }
22135             }.createDelegate(this);
22136             supr.setVisible.call(this, true, true, d, cb, e);
22137         }else{
22138             if(!v){
22139                 this.hideUnders(true);
22140             }
22141             var cb = c;
22142             if(a){
22143                 cb = function(){
22144                     this.hideAction();
22145                     if(c){
22146                         c();
22147                     }
22148                 }.createDelegate(this);
22149             }
22150             supr.setVisible.call(this, v, a, d, cb, e);
22151             if(v){
22152                 this.sync(true);
22153             }else if(!a){
22154                 this.hideAction();
22155             }
22156         }
22157     },
22158
22159     storeXY : function(xy){
22160         delete this.lastLT;
22161         this.lastXY = xy;
22162     },
22163
22164     storeLeftTop : function(left, top){
22165         delete this.lastXY;
22166         this.lastLT = [left, top];
22167     },
22168
22169     // private
22170     beforeFx : function(){
22171         this.beforeAction();
22172         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22173     },
22174
22175     // private
22176     afterFx : function(){
22177         Roo.Layer.superclass.afterFx.apply(this, arguments);
22178         this.sync(this.isVisible());
22179     },
22180
22181     // private
22182     beforeAction : function(){
22183         if(!this.updating && this.shadow){
22184             this.shadow.hide();
22185         }
22186     },
22187
22188     // overridden Element method
22189     setLeft : function(left){
22190         this.storeLeftTop(left, this.getTop(true));
22191         supr.setLeft.apply(this, arguments);
22192         this.sync();
22193     },
22194
22195     setTop : function(top){
22196         this.storeLeftTop(this.getLeft(true), top);
22197         supr.setTop.apply(this, arguments);
22198         this.sync();
22199     },
22200
22201     setLeftTop : function(left, top){
22202         this.storeLeftTop(left, top);
22203         supr.setLeftTop.apply(this, arguments);
22204         this.sync();
22205     },
22206
22207     setXY : function(xy, a, d, c, e){
22208         this.fixDisplay();
22209         this.beforeAction();
22210         this.storeXY(xy);
22211         var cb = this.createCB(c);
22212         supr.setXY.call(this, xy, a, d, cb, e);
22213         if(!a){
22214             cb();
22215         }
22216     },
22217
22218     // private
22219     createCB : function(c){
22220         var el = this;
22221         return function(){
22222             el.constrainXY();
22223             el.sync(true);
22224             if(c){
22225                 c();
22226             }
22227         };
22228     },
22229
22230     // overridden Element method
22231     setX : function(x, a, d, c, e){
22232         this.setXY([x, this.getY()], a, d, c, e);
22233     },
22234
22235     // overridden Element method
22236     setY : function(y, a, d, c, e){
22237         this.setXY([this.getX(), y], a, d, c, e);
22238     },
22239
22240     // overridden Element method
22241     setSize : function(w, h, a, d, c, e){
22242         this.beforeAction();
22243         var cb = this.createCB(c);
22244         supr.setSize.call(this, w, h, a, d, cb, e);
22245         if(!a){
22246             cb();
22247         }
22248     },
22249
22250     // overridden Element method
22251     setWidth : function(w, a, d, c, e){
22252         this.beforeAction();
22253         var cb = this.createCB(c);
22254         supr.setWidth.call(this, w, a, d, cb, e);
22255         if(!a){
22256             cb();
22257         }
22258     },
22259
22260     // overridden Element method
22261     setHeight : function(h, a, d, c, e){
22262         this.beforeAction();
22263         var cb = this.createCB(c);
22264         supr.setHeight.call(this, h, a, d, cb, e);
22265         if(!a){
22266             cb();
22267         }
22268     },
22269
22270     // overridden Element method
22271     setBounds : function(x, y, w, h, a, d, c, e){
22272         this.beforeAction();
22273         var cb = this.createCB(c);
22274         if(!a){
22275             this.storeXY([x, y]);
22276             supr.setXY.call(this, [x, y]);
22277             supr.setSize.call(this, w, h, a, d, cb, e);
22278             cb();
22279         }else{
22280             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22281         }
22282         return this;
22283     },
22284     
22285     /**
22286      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22287      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22288      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22289      * @param {Number} zindex The new z-index to set
22290      * @return {this} The Layer
22291      */
22292     setZIndex : function(zindex){
22293         this.zindex = zindex;
22294         this.setStyle("z-index", zindex + 2);
22295         if(this.shadow){
22296             this.shadow.setZIndex(zindex + 1);
22297         }
22298         if(this.shim){
22299             this.shim.setStyle("z-index", zindex);
22300         }
22301     }
22302 });
22303 })();/*
22304  * Based on:
22305  * Ext JS Library 1.1.1
22306  * Copyright(c) 2006-2007, Ext JS, LLC.
22307  *
22308  * Originally Released Under LGPL - original licence link has changed is not relivant.
22309  *
22310  * Fork - LGPL
22311  * <script type="text/javascript">
22312  */
22313
22314
22315 /**
22316  * @class Roo.Shadow
22317  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22318  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22319  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22320  * @constructor
22321  * Create a new Shadow
22322  * @param {Object} config The config object
22323  */
22324 Roo.Shadow = function(config){
22325     Roo.apply(this, config);
22326     if(typeof this.mode != "string"){
22327         this.mode = this.defaultMode;
22328     }
22329     var o = this.offset, a = {h: 0};
22330     var rad = Math.floor(this.offset/2);
22331     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22332         case "drop":
22333             a.w = 0;
22334             a.l = a.t = o;
22335             a.t -= 1;
22336             if(Roo.isIE){
22337                 a.l -= this.offset + rad;
22338                 a.t -= this.offset + rad;
22339                 a.w -= rad;
22340                 a.h -= rad;
22341                 a.t += 1;
22342             }
22343         break;
22344         case "sides":
22345             a.w = (o*2);
22346             a.l = -o;
22347             a.t = o-1;
22348             if(Roo.isIE){
22349                 a.l -= (this.offset - rad);
22350                 a.t -= this.offset + rad;
22351                 a.l += 1;
22352                 a.w -= (this.offset - rad)*2;
22353                 a.w -= rad + 1;
22354                 a.h -= 1;
22355             }
22356         break;
22357         case "frame":
22358             a.w = a.h = (o*2);
22359             a.l = a.t = -o;
22360             a.t += 1;
22361             a.h -= 2;
22362             if(Roo.isIE){
22363                 a.l -= (this.offset - rad);
22364                 a.t -= (this.offset - rad);
22365                 a.l += 1;
22366                 a.w -= (this.offset + rad + 1);
22367                 a.h -= (this.offset + rad);
22368                 a.h += 1;
22369             }
22370         break;
22371     };
22372
22373     this.adjusts = a;
22374 };
22375
22376 Roo.Shadow.prototype = {
22377     /**
22378      * @cfg {String} mode
22379      * The shadow display mode.  Supports the following options:<br />
22380      * sides: Shadow displays on both sides and bottom only<br />
22381      * frame: Shadow displays equally on all four sides<br />
22382      * drop: Traditional bottom-right drop shadow (default)
22383      */
22384     /**
22385      * @cfg {String} offset
22386      * The number of pixels to offset the shadow from the element (defaults to 4)
22387      */
22388     offset: 4,
22389
22390     // private
22391     defaultMode: "drop",
22392
22393     /**
22394      * Displays the shadow under the target element
22395      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22396      */
22397     show : function(target){
22398         target = Roo.get(target);
22399         if(!this.el){
22400             this.el = Roo.Shadow.Pool.pull();
22401             if(this.el.dom.nextSibling != target.dom){
22402                 this.el.insertBefore(target);
22403             }
22404         }
22405         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22406         if(Roo.isIE){
22407             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22408         }
22409         this.realign(
22410             target.getLeft(true),
22411             target.getTop(true),
22412             target.getWidth(),
22413             target.getHeight()
22414         );
22415         this.el.dom.style.display = "block";
22416     },
22417
22418     /**
22419      * Returns true if the shadow is visible, else false
22420      */
22421     isVisible : function(){
22422         return this.el ? true : false;  
22423     },
22424
22425     /**
22426      * Direct alignment when values are already available. Show must be called at least once before
22427      * calling this method to ensure it is initialized.
22428      * @param {Number} left The target element left position
22429      * @param {Number} top The target element top position
22430      * @param {Number} width The target element width
22431      * @param {Number} height The target element height
22432      */
22433     realign : function(l, t, w, h){
22434         if(!this.el){
22435             return;
22436         }
22437         var a = this.adjusts, d = this.el.dom, s = d.style;
22438         var iea = 0;
22439         s.left = (l+a.l)+"px";
22440         s.top = (t+a.t)+"px";
22441         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22442         if(s.width != sws || s.height != shs){
22443             s.width = sws;
22444             s.height = shs;
22445             if(!Roo.isIE){
22446                 var cn = d.childNodes;
22447                 var sww = Math.max(0, (sw-12))+"px";
22448                 cn[0].childNodes[1].style.width = sww;
22449                 cn[1].childNodes[1].style.width = sww;
22450                 cn[2].childNodes[1].style.width = sww;
22451                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22452             }
22453         }
22454     },
22455
22456     /**
22457      * Hides this shadow
22458      */
22459     hide : function(){
22460         if(this.el){
22461             this.el.dom.style.display = "none";
22462             Roo.Shadow.Pool.push(this.el);
22463             delete this.el;
22464         }
22465     },
22466
22467     /**
22468      * Adjust the z-index of this shadow
22469      * @param {Number} zindex The new z-index
22470      */
22471     setZIndex : function(z){
22472         this.zIndex = z;
22473         if(this.el){
22474             this.el.setStyle("z-index", z);
22475         }
22476     }
22477 };
22478
22479 // Private utility class that manages the internal Shadow cache
22480 Roo.Shadow.Pool = function(){
22481     var p = [];
22482     var markup = Roo.isIE ?
22483                  '<div class="x-ie-shadow"></div>' :
22484                  '<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>';
22485     return {
22486         pull : function(){
22487             var sh = p.shift();
22488             if(!sh){
22489                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22490                 sh.autoBoxAdjust = false;
22491             }
22492             return sh;
22493         },
22494
22495         push : function(sh){
22496             p.push(sh);
22497         }
22498     };
22499 }();/*
22500  * Based on:
22501  * Ext JS Library 1.1.1
22502  * Copyright(c) 2006-2007, Ext JS, LLC.
22503  *
22504  * Originally Released Under LGPL - original licence link has changed is not relivant.
22505  *
22506  * Fork - LGPL
22507  * <script type="text/javascript">
22508  */
22509
22510 /**
22511  * @class Roo.BoxComponent
22512  * @extends Roo.Component
22513  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22514  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22515  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22516  * layout containers.
22517  * @constructor
22518  * @param {Roo.Element/String/Object} config The configuration options.
22519  */
22520 Roo.BoxComponent = function(config){
22521     Roo.Component.call(this, config);
22522     this.addEvents({
22523         /**
22524          * @event resize
22525          * Fires after the component is resized.
22526              * @param {Roo.Component} this
22527              * @param {Number} adjWidth The box-adjusted width that was set
22528              * @param {Number} adjHeight The box-adjusted height that was set
22529              * @param {Number} rawWidth The width that was originally specified
22530              * @param {Number} rawHeight The height that was originally specified
22531              */
22532         resize : true,
22533         /**
22534          * @event move
22535          * Fires after the component is moved.
22536              * @param {Roo.Component} this
22537              * @param {Number} x The new x position
22538              * @param {Number} y The new y position
22539              */
22540         move : true
22541     });
22542 };
22543
22544 Roo.extend(Roo.BoxComponent, Roo.Component, {
22545     // private, set in afterRender to signify that the component has been rendered
22546     boxReady : false,
22547     // private, used to defer height settings to subclasses
22548     deferHeight: false,
22549     /** @cfg {Number} width
22550      * width (optional) size of component
22551      */
22552      /** @cfg {Number} height
22553      * height (optional) size of component
22554      */
22555      
22556     /**
22557      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22558      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22559      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22560      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22561      * @return {Roo.BoxComponent} this
22562      */
22563     setSize : function(w, h){
22564         // support for standard size objects
22565         if(typeof w == 'object'){
22566             h = w.height;
22567             w = w.width;
22568         }
22569         // not rendered
22570         if(!this.boxReady){
22571             this.width = w;
22572             this.height = h;
22573             return this;
22574         }
22575
22576         // prevent recalcs when not needed
22577         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22578             return this;
22579         }
22580         this.lastSize = {width: w, height: h};
22581
22582         var adj = this.adjustSize(w, h);
22583         var aw = adj.width, ah = adj.height;
22584         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22585             var rz = this.getResizeEl();
22586             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22587                 rz.setSize(aw, ah);
22588             }else if(!this.deferHeight && ah !== undefined){
22589                 rz.setHeight(ah);
22590             }else if(aw !== undefined){
22591                 rz.setWidth(aw);
22592             }
22593             this.onResize(aw, ah, w, h);
22594             this.fireEvent('resize', this, aw, ah, w, h);
22595         }
22596         return this;
22597     },
22598
22599     /**
22600      * Gets the current size of the component's underlying element.
22601      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22602      */
22603     getSize : function(){
22604         return this.el.getSize();
22605     },
22606
22607     /**
22608      * Gets the current XY position of the component's underlying element.
22609      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22610      * @return {Array} The XY position of the element (e.g., [100, 200])
22611      */
22612     getPosition : function(local){
22613         if(local === true){
22614             return [this.el.getLeft(true), this.el.getTop(true)];
22615         }
22616         return this.xy || this.el.getXY();
22617     },
22618
22619     /**
22620      * Gets the current box measurements of the component's underlying element.
22621      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22622      * @returns {Object} box An object in the format {x, y, width, height}
22623      */
22624     getBox : function(local){
22625         var s = this.el.getSize();
22626         if(local){
22627             s.x = this.el.getLeft(true);
22628             s.y = this.el.getTop(true);
22629         }else{
22630             var xy = this.xy || this.el.getXY();
22631             s.x = xy[0];
22632             s.y = xy[1];
22633         }
22634         return s;
22635     },
22636
22637     /**
22638      * Sets the current box measurements of the component's underlying element.
22639      * @param {Object} box An object in the format {x, y, width, height}
22640      * @returns {Roo.BoxComponent} this
22641      */
22642     updateBox : function(box){
22643         this.setSize(box.width, box.height);
22644         this.setPagePosition(box.x, box.y);
22645         return this;
22646     },
22647
22648     // protected
22649     getResizeEl : function(){
22650         return this.resizeEl || this.el;
22651     },
22652
22653     // protected
22654     getPositionEl : function(){
22655         return this.positionEl || this.el;
22656     },
22657
22658     /**
22659      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22660      * This method fires the move event.
22661      * @param {Number} left The new left
22662      * @param {Number} top The new top
22663      * @returns {Roo.BoxComponent} this
22664      */
22665     setPosition : function(x, y){
22666         this.x = x;
22667         this.y = y;
22668         if(!this.boxReady){
22669             return this;
22670         }
22671         var adj = this.adjustPosition(x, y);
22672         var ax = adj.x, ay = adj.y;
22673
22674         var el = this.getPositionEl();
22675         if(ax !== undefined || ay !== undefined){
22676             if(ax !== undefined && ay !== undefined){
22677                 el.setLeftTop(ax, ay);
22678             }else if(ax !== undefined){
22679                 el.setLeft(ax);
22680             }else if(ay !== undefined){
22681                 el.setTop(ay);
22682             }
22683             this.onPosition(ax, ay);
22684             this.fireEvent('move', this, ax, ay);
22685         }
22686         return this;
22687     },
22688
22689     /**
22690      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22691      * This method fires the move event.
22692      * @param {Number} x The new x position
22693      * @param {Number} y The new y position
22694      * @returns {Roo.BoxComponent} this
22695      */
22696     setPagePosition : function(x, y){
22697         this.pageX = x;
22698         this.pageY = y;
22699         if(!this.boxReady){
22700             return;
22701         }
22702         if(x === undefined || y === undefined){ // cannot translate undefined points
22703             return;
22704         }
22705         var p = this.el.translatePoints(x, y);
22706         this.setPosition(p.left, p.top);
22707         return this;
22708     },
22709
22710     // private
22711     onRender : function(ct, position){
22712         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22713         if(this.resizeEl){
22714             this.resizeEl = Roo.get(this.resizeEl);
22715         }
22716         if(this.positionEl){
22717             this.positionEl = Roo.get(this.positionEl);
22718         }
22719     },
22720
22721     // private
22722     afterRender : function(){
22723         Roo.BoxComponent.superclass.afterRender.call(this);
22724         this.boxReady = true;
22725         this.setSize(this.width, this.height);
22726         if(this.x || this.y){
22727             this.setPosition(this.x, this.y);
22728         }
22729         if(this.pageX || this.pageY){
22730             this.setPagePosition(this.pageX, this.pageY);
22731         }
22732     },
22733
22734     /**
22735      * Force the component's size to recalculate based on the underlying element's current height and width.
22736      * @returns {Roo.BoxComponent} this
22737      */
22738     syncSize : function(){
22739         delete this.lastSize;
22740         this.setSize(this.el.getWidth(), this.el.getHeight());
22741         return this;
22742     },
22743
22744     /**
22745      * Called after the component is resized, this method is empty by default but can be implemented by any
22746      * subclass that needs to perform custom logic after a resize occurs.
22747      * @param {Number} adjWidth The box-adjusted width that was set
22748      * @param {Number} adjHeight The box-adjusted height that was set
22749      * @param {Number} rawWidth The width that was originally specified
22750      * @param {Number} rawHeight The height that was originally specified
22751      */
22752     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22753
22754     },
22755
22756     /**
22757      * Called after the component is moved, this method is empty by default but can be implemented by any
22758      * subclass that needs to perform custom logic after a move occurs.
22759      * @param {Number} x The new x position
22760      * @param {Number} y The new y position
22761      */
22762     onPosition : function(x, y){
22763
22764     },
22765
22766     // private
22767     adjustSize : function(w, h){
22768         if(this.autoWidth){
22769             w = 'auto';
22770         }
22771         if(this.autoHeight){
22772             h = 'auto';
22773         }
22774         return {width : w, height: h};
22775     },
22776
22777     // private
22778     adjustPosition : function(x, y){
22779         return {x : x, y: y};
22780     }
22781 });/*
22782  * Based on:
22783  * Ext JS Library 1.1.1
22784  * Copyright(c) 2006-2007, Ext JS, LLC.
22785  *
22786  * Originally Released Under LGPL - original licence link has changed is not relivant.
22787  *
22788  * Fork - LGPL
22789  * <script type="text/javascript">
22790  */
22791
22792
22793 /**
22794  * @class Roo.SplitBar
22795  * @extends Roo.util.Observable
22796  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22797  * <br><br>
22798  * Usage:
22799  * <pre><code>
22800 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22801                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22802 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22803 split.minSize = 100;
22804 split.maxSize = 600;
22805 split.animate = true;
22806 split.on('moved', splitterMoved);
22807 </code></pre>
22808  * @constructor
22809  * Create a new SplitBar
22810  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22811  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22812  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22813  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22814                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22815                         position of the SplitBar).
22816  */
22817 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22818     
22819     /** @private */
22820     this.el = Roo.get(dragElement, true);
22821     this.el.dom.unselectable = "on";
22822     /** @private */
22823     this.resizingEl = Roo.get(resizingElement, true);
22824
22825     /**
22826      * @private
22827      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22828      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22829      * @type Number
22830      */
22831     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22832     
22833     /**
22834      * The minimum size of the resizing element. (Defaults to 0)
22835      * @type Number
22836      */
22837     this.minSize = 0;
22838     
22839     /**
22840      * The maximum size of the resizing element. (Defaults to 2000)
22841      * @type Number
22842      */
22843     this.maxSize = 2000;
22844     
22845     /**
22846      * Whether to animate the transition to the new size
22847      * @type Boolean
22848      */
22849     this.animate = false;
22850     
22851     /**
22852      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22853      * @type Boolean
22854      */
22855     this.useShim = false;
22856     
22857     /** @private */
22858     this.shim = null;
22859     
22860     if(!existingProxy){
22861         /** @private */
22862         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22863     }else{
22864         this.proxy = Roo.get(existingProxy).dom;
22865     }
22866     /** @private */
22867     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22868     
22869     /** @private */
22870     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22871     
22872     /** @private */
22873     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22874     
22875     /** @private */
22876     this.dragSpecs = {};
22877     
22878     /**
22879      * @private The adapter to use to positon and resize elements
22880      */
22881     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22882     this.adapter.init(this);
22883     
22884     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22885         /** @private */
22886         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22887         this.el.addClass("x-splitbar-h");
22888     }else{
22889         /** @private */
22890         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22891         this.el.addClass("x-splitbar-v");
22892     }
22893     
22894     this.addEvents({
22895         /**
22896          * @event resize
22897          * Fires when the splitter is moved (alias for {@link #event-moved})
22898          * @param {Roo.SplitBar} this
22899          * @param {Number} newSize the new width or height
22900          */
22901         "resize" : true,
22902         /**
22903          * @event moved
22904          * Fires when the splitter is moved
22905          * @param {Roo.SplitBar} this
22906          * @param {Number} newSize the new width or height
22907          */
22908         "moved" : true,
22909         /**
22910          * @event beforeresize
22911          * Fires before the splitter is dragged
22912          * @param {Roo.SplitBar} this
22913          */
22914         "beforeresize" : true,
22915
22916         "beforeapply" : true
22917     });
22918
22919     Roo.util.Observable.call(this);
22920 };
22921
22922 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22923     onStartProxyDrag : function(x, y){
22924         this.fireEvent("beforeresize", this);
22925         if(!this.overlay){
22926             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22927             o.unselectable();
22928             o.enableDisplayMode("block");
22929             // all splitbars share the same overlay
22930             Roo.SplitBar.prototype.overlay = o;
22931         }
22932         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22933         this.overlay.show();
22934         Roo.get(this.proxy).setDisplayed("block");
22935         var size = this.adapter.getElementSize(this);
22936         this.activeMinSize = this.getMinimumSize();;
22937         this.activeMaxSize = this.getMaximumSize();;
22938         var c1 = size - this.activeMinSize;
22939         var c2 = Math.max(this.activeMaxSize - size, 0);
22940         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22941             this.dd.resetConstraints();
22942             this.dd.setXConstraint(
22943                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22944                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22945             );
22946             this.dd.setYConstraint(0, 0);
22947         }else{
22948             this.dd.resetConstraints();
22949             this.dd.setXConstraint(0, 0);
22950             this.dd.setYConstraint(
22951                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22952                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22953             );
22954          }
22955         this.dragSpecs.startSize = size;
22956         this.dragSpecs.startPoint = [x, y];
22957         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22958     },
22959     
22960     /** 
22961      * @private Called after the drag operation by the DDProxy
22962      */
22963     onEndProxyDrag : function(e){
22964         Roo.get(this.proxy).setDisplayed(false);
22965         var endPoint = Roo.lib.Event.getXY(e);
22966         if(this.overlay){
22967             this.overlay.hide();
22968         }
22969         var newSize;
22970         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22971             newSize = this.dragSpecs.startSize + 
22972                 (this.placement == Roo.SplitBar.LEFT ?
22973                     endPoint[0] - this.dragSpecs.startPoint[0] :
22974                     this.dragSpecs.startPoint[0] - endPoint[0]
22975                 );
22976         }else{
22977             newSize = this.dragSpecs.startSize + 
22978                 (this.placement == Roo.SplitBar.TOP ?
22979                     endPoint[1] - this.dragSpecs.startPoint[1] :
22980                     this.dragSpecs.startPoint[1] - endPoint[1]
22981                 );
22982         }
22983         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22984         if(newSize != this.dragSpecs.startSize){
22985             if(this.fireEvent('beforeapply', this, newSize) !== false){
22986                 this.adapter.setElementSize(this, newSize);
22987                 this.fireEvent("moved", this, newSize);
22988                 this.fireEvent("resize", this, newSize);
22989             }
22990         }
22991     },
22992     
22993     /**
22994      * Get the adapter this SplitBar uses
22995      * @return The adapter object
22996      */
22997     getAdapter : function(){
22998         return this.adapter;
22999     },
23000     
23001     /**
23002      * Set the adapter this SplitBar uses
23003      * @param {Object} adapter A SplitBar adapter object
23004      */
23005     setAdapter : function(adapter){
23006         this.adapter = adapter;
23007         this.adapter.init(this);
23008     },
23009     
23010     /**
23011      * Gets the minimum size for the resizing element
23012      * @return {Number} The minimum size
23013      */
23014     getMinimumSize : function(){
23015         return this.minSize;
23016     },
23017     
23018     /**
23019      * Sets the minimum size for the resizing element
23020      * @param {Number} minSize The minimum size
23021      */
23022     setMinimumSize : function(minSize){
23023         this.minSize = minSize;
23024     },
23025     
23026     /**
23027      * Gets the maximum size for the resizing element
23028      * @return {Number} The maximum size
23029      */
23030     getMaximumSize : function(){
23031         return this.maxSize;
23032     },
23033     
23034     /**
23035      * Sets the maximum size for the resizing element
23036      * @param {Number} maxSize The maximum size
23037      */
23038     setMaximumSize : function(maxSize){
23039         this.maxSize = maxSize;
23040     },
23041     
23042     /**
23043      * Sets the initialize size for the resizing element
23044      * @param {Number} size The initial size
23045      */
23046     setCurrentSize : function(size){
23047         var oldAnimate = this.animate;
23048         this.animate = false;
23049         this.adapter.setElementSize(this, size);
23050         this.animate = oldAnimate;
23051     },
23052     
23053     /**
23054      * Destroy this splitbar. 
23055      * @param {Boolean} removeEl True to remove the element
23056      */
23057     destroy : function(removeEl){
23058         if(this.shim){
23059             this.shim.remove();
23060         }
23061         this.dd.unreg();
23062         this.proxy.parentNode.removeChild(this.proxy);
23063         if(removeEl){
23064             this.el.remove();
23065         }
23066     }
23067 });
23068
23069 /**
23070  * @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.
23071  */
23072 Roo.SplitBar.createProxy = function(dir){
23073     var proxy = new Roo.Element(document.createElement("div"));
23074     proxy.unselectable();
23075     var cls = 'x-splitbar-proxy';
23076     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23077     document.body.appendChild(proxy.dom);
23078     return proxy.dom;
23079 };
23080
23081 /** 
23082  * @class Roo.SplitBar.BasicLayoutAdapter
23083  * Default Adapter. It assumes the splitter and resizing element are not positioned
23084  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23085  */
23086 Roo.SplitBar.BasicLayoutAdapter = function(){
23087 };
23088
23089 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23090     // do nothing for now
23091     init : function(s){
23092     
23093     },
23094     /**
23095      * Called before drag operations to get the current size of the resizing element. 
23096      * @param {Roo.SplitBar} s The SplitBar using this adapter
23097      */
23098      getElementSize : function(s){
23099         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23100             return s.resizingEl.getWidth();
23101         }else{
23102             return s.resizingEl.getHeight();
23103         }
23104     },
23105     
23106     /**
23107      * Called after drag operations to set the size of the resizing element.
23108      * @param {Roo.SplitBar} s The SplitBar using this adapter
23109      * @param {Number} newSize The new size to set
23110      * @param {Function} onComplete A function to be invoked when resizing is complete
23111      */
23112     setElementSize : function(s, newSize, onComplete){
23113         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23114             if(!s.animate){
23115                 s.resizingEl.setWidth(newSize);
23116                 if(onComplete){
23117                     onComplete(s, newSize);
23118                 }
23119             }else{
23120                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23121             }
23122         }else{
23123             
23124             if(!s.animate){
23125                 s.resizingEl.setHeight(newSize);
23126                 if(onComplete){
23127                     onComplete(s, newSize);
23128                 }
23129             }else{
23130                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23131             }
23132         }
23133     }
23134 };
23135
23136 /** 
23137  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23138  * @extends Roo.SplitBar.BasicLayoutAdapter
23139  * Adapter that  moves the splitter element to align with the resized sizing element. 
23140  * Used with an absolute positioned SplitBar.
23141  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23142  * document.body, make sure you assign an id to the body element.
23143  */
23144 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23145     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23146     this.container = Roo.get(container);
23147 };
23148
23149 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23150     init : function(s){
23151         this.basic.init(s);
23152     },
23153     
23154     getElementSize : function(s){
23155         return this.basic.getElementSize(s);
23156     },
23157     
23158     setElementSize : function(s, newSize, onComplete){
23159         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23160     },
23161     
23162     moveSplitter : function(s){
23163         var yes = Roo.SplitBar;
23164         switch(s.placement){
23165             case yes.LEFT:
23166                 s.el.setX(s.resizingEl.getRight());
23167                 break;
23168             case yes.RIGHT:
23169                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23170                 break;
23171             case yes.TOP:
23172                 s.el.setY(s.resizingEl.getBottom());
23173                 break;
23174             case yes.BOTTOM:
23175                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23176                 break;
23177         }
23178     }
23179 };
23180
23181 /**
23182  * Orientation constant - Create a vertical SplitBar
23183  * @static
23184  * @type Number
23185  */
23186 Roo.SplitBar.VERTICAL = 1;
23187
23188 /**
23189  * Orientation constant - Create a horizontal SplitBar
23190  * @static
23191  * @type Number
23192  */
23193 Roo.SplitBar.HORIZONTAL = 2;
23194
23195 /**
23196  * Placement constant - The resizing element is to the left of the splitter element
23197  * @static
23198  * @type Number
23199  */
23200 Roo.SplitBar.LEFT = 1;
23201
23202 /**
23203  * Placement constant - The resizing element is to the right of the splitter element
23204  * @static
23205  * @type Number
23206  */
23207 Roo.SplitBar.RIGHT = 2;
23208
23209 /**
23210  * Placement constant - The resizing element is positioned above the splitter element
23211  * @static
23212  * @type Number
23213  */
23214 Roo.SplitBar.TOP = 3;
23215
23216 /**
23217  * Placement constant - The resizing element is positioned under splitter element
23218  * @static
23219  * @type Number
23220  */
23221 Roo.SplitBar.BOTTOM = 4;
23222 /*
23223  * Based on:
23224  * Ext JS Library 1.1.1
23225  * Copyright(c) 2006-2007, Ext JS, LLC.
23226  *
23227  * Originally Released Under LGPL - original licence link has changed is not relivant.
23228  *
23229  * Fork - LGPL
23230  * <script type="text/javascript">
23231  */
23232
23233 /**
23234  * @class Roo.View
23235  * @extends Roo.util.Observable
23236  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23237  * This class also supports single and multi selection modes. <br>
23238  * Create a data model bound view:
23239  <pre><code>
23240  var store = new Roo.data.Store(...);
23241
23242  var view = new Roo.View({
23243     el : "my-element",
23244     template : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23245  
23246     singleSelect: true,
23247     selectedClass: "ydataview-selected",
23248     store: store
23249  });
23250
23251  // listen for node click?
23252  view.on("click", function(vw, index, node, e){
23253  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23254  });
23255
23256  // load XML data
23257  dataModel.load("foobar.xml");
23258  </code></pre>
23259  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23260  * <br><br>
23261  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23262  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23263  * 
23264  * Note: old style constructor is still suported (container, template, config)
23265  * 
23266  * @constructor
23267  * Create a new View
23268  * @param {Object} config The config object
23269  * 
23270  */
23271 Roo.View = function(config, depreciated_tpl, depreciated_config){
23272     
23273     if (typeof(depreciated_tpl) == 'undefined') {
23274         // new way.. - universal constructor.
23275         Roo.apply(this, config);
23276         this.el  = Roo.get(this.el);
23277     } else {
23278         // old format..
23279         this.el  = Roo.get(config);
23280         this.tpl = depreciated_tpl;
23281         Roo.apply(this, depreciated_config);
23282     }
23283      
23284     
23285     if(typeof(this.tpl) == "string"){
23286         this.tpl = new Roo.Template(this.tpl);
23287     } 
23288     
23289     
23290     this.tpl.compile();
23291    
23292
23293      
23294     /** @private */
23295     this.addEvents({
23296     /**
23297      * @event beforeclick
23298      * Fires before a click is processed. Returns false to cancel the default action.
23299      * @param {Roo.View} this
23300      * @param {Number} index The index of the target node
23301      * @param {HTMLElement} node The target node
23302      * @param {Roo.EventObject} e The raw event object
23303      */
23304         "beforeclick" : true,
23305     /**
23306      * @event click
23307      * Fires when a template node is clicked.
23308      * @param {Roo.View} this
23309      * @param {Number} index The index of the target node
23310      * @param {HTMLElement} node The target node
23311      * @param {Roo.EventObject} e The raw event object
23312      */
23313         "click" : true,
23314     /**
23315      * @event dblclick
23316      * Fires when a template node is double clicked.
23317      * @param {Roo.View} this
23318      * @param {Number} index The index of the target node
23319      * @param {HTMLElement} node The target node
23320      * @param {Roo.EventObject} e The raw event object
23321      */
23322         "dblclick" : true,
23323     /**
23324      * @event contextmenu
23325      * Fires when a template node is right clicked.
23326      * @param {Roo.View} this
23327      * @param {Number} index The index of the target node
23328      * @param {HTMLElement} node The target node
23329      * @param {Roo.EventObject} e The raw event object
23330      */
23331         "contextmenu" : true,
23332     /**
23333      * @event selectionchange
23334      * Fires when the selected nodes change.
23335      * @param {Roo.View} this
23336      * @param {Array} selections Array of the selected nodes
23337      */
23338         "selectionchange" : true,
23339
23340     /**
23341      * @event beforeselect
23342      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23343      * @param {Roo.View} this
23344      * @param {HTMLElement} node The node to be selected
23345      * @param {Array} selections Array of currently selected nodes
23346      */
23347         "beforeselect" : true
23348     });
23349
23350     this.el.on({
23351         "click": this.onClick,
23352         "dblclick": this.onDblClick,
23353         "contextmenu": this.onContextMenu,
23354         scope:this
23355     });
23356
23357     this.selections = [];
23358     this.nodes = [];
23359     this.cmp = new Roo.CompositeElementLite([]);
23360     if(this.store){
23361         this.store = Roo.factory(this.store, Roo.data);
23362         this.setStore(this.store, true);
23363     }
23364     Roo.View.superclass.constructor.call(this);
23365 };
23366
23367 Roo.extend(Roo.View, Roo.util.Observable, {
23368     
23369      /**
23370      * @cfg {Roo.data.Store} Data store to load data from.
23371      */
23372     el : false,
23373     
23374     /**
23375      * @cfg {String|Roo.Element} The container element.
23376      */
23377     el : '',
23378     
23379     /**
23380      * @cfg {String|Roo.DomHelper.Template} The template used by this View 
23381      */
23382     this.tpl : false,
23383     
23384     /**
23385      * @cfg {Roo.DomHelper.Template} The css class to add to selected nodes
23386      */
23387     selectedClass : "x-view-selected",
23388      /**
23389      * @cfg {String} The empty text to show when nothing is loaded.
23390      */
23391     emptyText : "",
23392     /**
23393      * Returns the element this view is bound to.
23394      * @return {Roo.Element}
23395      */
23396     getEl : function(){
23397         return this.el;
23398     },
23399
23400     /**
23401      * Refreshes the view.
23402      */
23403     refresh : function(){
23404         var t = this.tpl;
23405         this.clearSelections();
23406         this.el.update("");
23407         var html = [];
23408         var records = this.store.getRange();
23409         if(records.length < 1){
23410             this.el.update(this.emptyText);
23411             return;
23412         }
23413         for(var i = 0, len = records.length; i < len; i++){
23414             var data = this.prepareData(records[i].data, i, records[i]);
23415             html[html.length] = t.apply(data);
23416         }
23417         this.el.update(html.join(""));
23418         this.nodes = this.el.dom.childNodes;
23419         this.updateIndexes(0);
23420     },
23421
23422     /**
23423      * Function to override to reformat the data that is sent to
23424      * the template for each node.
23425      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23426      * a JSON object for an UpdateManager bound view).
23427      */
23428     prepareData : function(data){
23429         return data;
23430     },
23431
23432     onUpdate : function(ds, record){
23433         this.clearSelections();
23434         var index = this.store.indexOf(record);
23435         var n = this.nodes[index];
23436         this.tpl.insertBefore(n, this.prepareData(record.data));
23437         n.parentNode.removeChild(n);
23438         this.updateIndexes(index, index);
23439     },
23440
23441     onAdd : function(ds, records, index){
23442         this.clearSelections();
23443         if(this.nodes.length == 0){
23444             this.refresh();
23445             return;
23446         }
23447         var n = this.nodes[index];
23448         for(var i = 0, len = records.length; i < len; i++){
23449             var d = this.prepareData(records[i].data);
23450             if(n){
23451                 this.tpl.insertBefore(n, d);
23452             }else{
23453                 this.tpl.append(this.el, d);
23454             }
23455         }
23456         this.updateIndexes(index);
23457     },
23458
23459     onRemove : function(ds, record, index){
23460         this.clearSelections();
23461         this.el.dom.removeChild(this.nodes[index]);
23462         this.updateIndexes(index);
23463     },
23464
23465     /**
23466      * Refresh an individual node.
23467      * @param {Number} index
23468      */
23469     refreshNode : function(index){
23470         this.onUpdate(this.store, this.store.getAt(index));
23471     },
23472
23473     updateIndexes : function(startIndex, endIndex){
23474         var ns = this.nodes;
23475         startIndex = startIndex || 0;
23476         endIndex = endIndex || ns.length - 1;
23477         for(var i = startIndex; i <= endIndex; i++){
23478             ns[i].nodeIndex = i;
23479         }
23480     },
23481
23482     /**
23483      * Changes the data store this view uses and refresh the view.
23484      * @param {Store} store
23485      */
23486     setStore : function(store, initial){
23487         if(!initial && this.store){
23488             this.store.un("datachanged", this.refresh);
23489             this.store.un("add", this.onAdd);
23490             this.store.un("remove", this.onRemove);
23491             this.store.un("update", this.onUpdate);
23492             this.store.un("clear", this.refresh);
23493         }
23494         if(store){
23495           
23496             store.on("datachanged", this.refresh, this);
23497             store.on("add", this.onAdd, this);
23498             store.on("remove", this.onRemove, this);
23499             store.on("update", this.onUpdate, this);
23500             store.on("clear", this.refresh, this);
23501         }
23502         
23503         if(store){
23504             this.refresh();
23505         }
23506     },
23507
23508     /**
23509      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23510      * @param {HTMLElement} node
23511      * @return {HTMLElement} The template node
23512      */
23513     findItemFromChild : function(node){
23514         var el = this.el.dom;
23515         if(!node || node.parentNode == el){
23516                     return node;
23517             }
23518             var p = node.parentNode;
23519             while(p && p != el){
23520             if(p.parentNode == el){
23521                 return p;
23522             }
23523             p = p.parentNode;
23524         }
23525             return null;
23526     },
23527
23528     /** @ignore */
23529     onClick : function(e){
23530         var item = this.findItemFromChild(e.getTarget());
23531         if(item){
23532             var index = this.indexOf(item);
23533             if(this.onItemClick(item, index, e) !== false){
23534                 this.fireEvent("click", this, index, item, e);
23535             }
23536         }else{
23537             this.clearSelections();
23538         }
23539     },
23540
23541     /** @ignore */
23542     onContextMenu : function(e){
23543         var item = this.findItemFromChild(e.getTarget());
23544         if(item){
23545             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23546         }
23547     },
23548
23549     /** @ignore */
23550     onDblClick : function(e){
23551         var item = this.findItemFromChild(e.getTarget());
23552         if(item){
23553             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23554         }
23555     },
23556
23557     onItemClick : function(item, index, e){
23558         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23559             return false;
23560         }
23561         if(this.multiSelect || this.singleSelect){
23562             if(this.multiSelect && e.shiftKey && this.lastSelection){
23563                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23564             }else{
23565                 this.select(item, this.multiSelect && e.ctrlKey);
23566                 this.lastSelection = item;
23567             }
23568             e.preventDefault();
23569         }
23570         return true;
23571     },
23572
23573     /**
23574      * Get the number of selected nodes.
23575      * @return {Number}
23576      */
23577     getSelectionCount : function(){
23578         return this.selections.length;
23579     },
23580
23581     /**
23582      * Get the currently selected nodes.
23583      * @return {Array} An array of HTMLElements
23584      */
23585     getSelectedNodes : function(){
23586         return this.selections;
23587     },
23588
23589     /**
23590      * Get the indexes of the selected nodes.
23591      * @return {Array}
23592      */
23593     getSelectedIndexes : function(){
23594         var indexes = [], s = this.selections;
23595         for(var i = 0, len = s.length; i < len; i++){
23596             indexes.push(s[i].nodeIndex);
23597         }
23598         return indexes;
23599     },
23600
23601     /**
23602      * Clear all selections
23603      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23604      */
23605     clearSelections : function(suppressEvent){
23606         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23607             this.cmp.elements = this.selections;
23608             this.cmp.removeClass(this.selectedClass);
23609             this.selections = [];
23610             if(!suppressEvent){
23611                 this.fireEvent("selectionchange", this, this.selections);
23612             }
23613         }
23614     },
23615
23616     /**
23617      * Returns true if the passed node is selected
23618      * @param {HTMLElement/Number} node The node or node index
23619      * @return {Boolean}
23620      */
23621     isSelected : function(node){
23622         var s = this.selections;
23623         if(s.length < 1){
23624             return false;
23625         }
23626         node = this.getNode(node);
23627         return s.indexOf(node) !== -1;
23628     },
23629
23630     /**
23631      * Selects nodes.
23632      * @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
23633      * @param {Boolean} keepExisting (optional) true to keep existing selections
23634      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23635      */
23636     select : function(nodeInfo, keepExisting, suppressEvent){
23637         if(nodeInfo instanceof Array){
23638             if(!keepExisting){
23639                 this.clearSelections(true);
23640             }
23641             for(var i = 0, len = nodeInfo.length; i < len; i++){
23642                 this.select(nodeInfo[i], true, true);
23643             }
23644         } else{
23645             var node = this.getNode(nodeInfo);
23646             if(node && !this.isSelected(node)){
23647                 if(!keepExisting){
23648                     this.clearSelections(true);
23649                 }
23650                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23651                     Roo.fly(node).addClass(this.selectedClass);
23652                     this.selections.push(node);
23653                     if(!suppressEvent){
23654                         this.fireEvent("selectionchange", this, this.selections);
23655                     }
23656                 }
23657             }
23658         }
23659     },
23660
23661     /**
23662      * Gets a template node.
23663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23664      * @return {HTMLElement} The node or null if it wasn't found
23665      */
23666     getNode : function(nodeInfo){
23667         if(typeof nodeInfo == "string"){
23668             return document.getElementById(nodeInfo);
23669         }else if(typeof nodeInfo == "number"){
23670             return this.nodes[nodeInfo];
23671         }
23672         return nodeInfo;
23673     },
23674
23675     /**
23676      * Gets a range template nodes.
23677      * @param {Number} startIndex
23678      * @param {Number} endIndex
23679      * @return {Array} An array of nodes
23680      */
23681     getNodes : function(start, end){
23682         var ns = this.nodes;
23683         start = start || 0;
23684         end = typeof end == "undefined" ? ns.length - 1 : end;
23685         var nodes = [];
23686         if(start <= end){
23687             for(var i = start; i <= end; i++){
23688                 nodes.push(ns[i]);
23689             }
23690         } else{
23691             for(var i = start; i >= end; i--){
23692                 nodes.push(ns[i]);
23693             }
23694         }
23695         return nodes;
23696     },
23697
23698     /**
23699      * Finds the index of the passed node
23700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23701      * @return {Number} The index of the node or -1
23702      */
23703     indexOf : function(node){
23704         node = this.getNode(node);
23705         if(typeof node.nodeIndex == "number"){
23706             return node.nodeIndex;
23707         }
23708         var ns = this.nodes;
23709         for(var i = 0, len = ns.length; i < len; i++){
23710             if(ns[i] == node){
23711                 return i;
23712             }
23713         }
23714         return -1;
23715     }
23716 });
23717 /*
23718  * Based on:
23719  * Ext JS Library 1.1.1
23720  * Copyright(c) 2006-2007, Ext JS, LLC.
23721  *
23722  * Originally Released Under LGPL - original licence link has changed is not relivant.
23723  *
23724  * Fork - LGPL
23725  * <script type="text/javascript">
23726  */
23727
23728 /**
23729  * @class Roo.JsonView
23730  * @extends Roo.View
23731  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23732 <pre><code>
23733 var view = new Roo.JsonView({
23734     container: "my-element",
23735     template: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23736     multiSelect: true, 
23737     jsonRoot: "data" 
23738 });
23739
23740 // listen for node click?
23741 view.on("click", function(vw, index, node, e){
23742     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23743 });
23744
23745 // direct load of JSON data
23746 view.load("foobar.php");
23747
23748 // Example from my blog list
23749 var tpl = new Roo.Template(
23750     '&lt;div class="entry"&gt;' +
23751     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23752     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23753     "&lt;/div&gt;&lt;hr /&gt;"
23754 );
23755
23756 var moreView = new Roo.JsonView({
23757     container :  "entry-list", 
23758     template : tpl,
23759     jsonRoot: "posts"
23760 });
23761 moreView.on("beforerender", this.sortEntries, this);
23762 moreView.load({
23763     url: "/blog/get-posts.php",
23764     params: "allposts=true",
23765     text: "Loading Blog Entries..."
23766 });
23767 </code></pre>
23768
23769 * Note: old code is supported with arguments : (container, template, config)
23770
23771
23772  * @constructor
23773  * Create a new JsonView
23774  * 
23775  * @param {Object} config The config object
23776  * 
23777  */
23778 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23779     
23780     
23781     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23782
23783     var um = this.el.getUpdateManager();
23784     um.setRenderer(this);
23785     um.on("update", this.onLoad, this);
23786     um.on("failure", this.onLoadException, this);
23787
23788     /**
23789      * @event beforerender
23790      * Fires before rendering of the downloaded JSON data.
23791      * @param {Roo.JsonView} this
23792      * @param {Object} data The JSON data loaded
23793      */
23794     /**
23795      * @event load
23796      * Fires when data is loaded.
23797      * @param {Roo.JsonView} this
23798      * @param {Object} data The JSON data loaded
23799      * @param {Object} response The raw Connect response object
23800      */
23801     /**
23802      * @event loadexception
23803      * Fires when loading fails.
23804      * @param {Roo.JsonView} this
23805      * @param {Object} response The raw Connect response object
23806      */
23807     this.addEvents({
23808         'beforerender' : true,
23809         'load' : true,
23810         'loadexception' : true
23811     });
23812 };
23813 Roo.extend(Roo.JsonView, Roo.View, {
23814     /**
23815      * 
23816      * @cfg {String} The root property in the loaded JSON object that contains the data
23817      */
23818     jsonRoot : "",
23819
23820     /**
23821      * Refreshes the view.
23822      */
23823     refresh : function(){
23824         this.clearSelections();
23825         this.el.update("");
23826         var html = [];
23827         var o = this.jsonData;
23828         if(o && o.length > 0){
23829             for(var i = 0, len = o.length; i < len; i++){
23830                 var data = this.prepareData(o[i], i, o);
23831                 html[html.length] = this.tpl.apply(data);
23832             }
23833         }else{
23834             html.push(this.emptyText);
23835         }
23836         this.el.update(html.join(""));
23837         this.nodes = this.el.dom.childNodes;
23838         this.updateIndexes(0);
23839     },
23840
23841     /**
23842      * 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.
23843      * @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:
23844      <pre><code>
23845      view.load({
23846          url: "your-url.php",
23847          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23848          callback: yourFunction,
23849          scope: yourObject, //(optional scope)
23850          discardUrl: false,
23851          nocache: false,
23852          text: "Loading...",
23853          timeout: 30,
23854          scripts: false
23855      });
23856      </code></pre>
23857      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23858      * 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.
23859      * @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}
23860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23861      * @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.
23862      */
23863     load : function(){
23864         var um = this.el.getUpdateManager();
23865         um.update.apply(um, arguments);
23866     },
23867
23868     render : function(el, response){
23869         this.clearSelections();
23870         this.el.update("");
23871         var o;
23872         try{
23873             o = Roo.util.JSON.decode(response.responseText);
23874             if(this.jsonRoot){
23875                 
23876                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23877             }
23878         } catch(e){
23879         }
23880         /**
23881          * The current JSON data or null
23882          */
23883         this.jsonData = o;
23884         this.beforeRender();
23885         this.refresh();
23886     },
23887
23888 /**
23889  * Get the number of records in the current JSON dataset
23890  * @return {Number}
23891  */
23892     getCount : function(){
23893         return this.jsonData ? this.jsonData.length : 0;
23894     },
23895
23896 /**
23897  * Returns the JSON object for the specified node(s)
23898  * @param {HTMLElement/Array} node The node or an array of nodes
23899  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23900  * you get the JSON object for the node
23901  */
23902     getNodeData : function(node){
23903         if(node instanceof Array){
23904             var data = [];
23905             for(var i = 0, len = node.length; i < len; i++){
23906                 data.push(this.getNodeData(node[i]));
23907             }
23908             return data;
23909         }
23910         return this.jsonData[this.indexOf(node)] || null;
23911     },
23912
23913     beforeRender : function(){
23914         this.snapshot = this.jsonData;
23915         if(this.sortInfo){
23916             this.sort.apply(this, this.sortInfo);
23917         }
23918         this.fireEvent("beforerender", this, this.jsonData);
23919     },
23920
23921     onLoad : function(el, o){
23922         this.fireEvent("load", this, this.jsonData, o);
23923     },
23924
23925     onLoadException : function(el, o){
23926         this.fireEvent("loadexception", this, o);
23927     },
23928
23929 /**
23930  * Filter the data by a specific property.
23931  * @param {String} property A property on your JSON objects
23932  * @param {String/RegExp} value Either string that the property values
23933  * should start with, or a RegExp to test against the property
23934  */
23935     filter : function(property, value){
23936         if(this.jsonData){
23937             var data = [];
23938             var ss = this.snapshot;
23939             if(typeof value == "string"){
23940                 var vlen = value.length;
23941                 if(vlen == 0){
23942                     this.clearFilter();
23943                     return;
23944                 }
23945                 value = value.toLowerCase();
23946                 for(var i = 0, len = ss.length; i < len; i++){
23947                     var o = ss[i];
23948                     if(o[property].substr(0, vlen).toLowerCase() == value){
23949                         data.push(o);
23950                     }
23951                 }
23952             } else if(value.exec){ // regex?
23953                 for(var i = 0, len = ss.length; i < len; i++){
23954                     var o = ss[i];
23955                     if(value.test(o[property])){
23956                         data.push(o);
23957                     }
23958                 }
23959             } else{
23960                 return;
23961             }
23962             this.jsonData = data;
23963             this.refresh();
23964         }
23965     },
23966
23967 /**
23968  * Filter by a function. The passed function will be called with each
23969  * object in the current dataset. If the function returns true the value is kept,
23970  * otherwise it is filtered.
23971  * @param {Function} fn
23972  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23973  */
23974     filterBy : function(fn, scope){
23975         if(this.jsonData){
23976             var data = [];
23977             var ss = this.snapshot;
23978             for(var i = 0, len = ss.length; i < len; i++){
23979                 var o = ss[i];
23980                 if(fn.call(scope || this, o)){
23981                     data.push(o);
23982                 }
23983             }
23984             this.jsonData = data;
23985             this.refresh();
23986         }
23987     },
23988
23989 /**
23990  * Clears the current filter.
23991  */
23992     clearFilter : function(){
23993         if(this.snapshot && this.jsonData != this.snapshot){
23994             this.jsonData = this.snapshot;
23995             this.refresh();
23996         }
23997     },
23998
23999
24000 /**
24001  * Sorts the data for this view and refreshes it.
24002  * @param {String} property A property on your JSON objects to sort on
24003  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24004  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24005  */
24006     sort : function(property, dir, sortType){
24007         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24008         if(this.jsonData){
24009             var p = property;
24010             var dsc = dir && dir.toLowerCase() == "desc";
24011             var f = function(o1, o2){
24012                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24013                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24014                 ;
24015                 if(v1 < v2){
24016                     return dsc ? +1 : -1;
24017                 } else if(v1 > v2){
24018                     return dsc ? -1 : +1;
24019                 } else{
24020                     return 0;
24021                 }
24022             };
24023             this.jsonData.sort(f);
24024             this.refresh();
24025             if(this.jsonData != this.snapshot){
24026                 this.snapshot.sort(f);
24027             }
24028         }
24029     }
24030 });/*
24031  * Based on:
24032  * Ext JS Library 1.1.1
24033  * Copyright(c) 2006-2007, Ext JS, LLC.
24034  *
24035  * Originally Released Under LGPL - original licence link has changed is not relivant.
24036  *
24037  * Fork - LGPL
24038  * <script type="text/javascript">
24039  */
24040  
24041
24042 /**
24043  * @class Roo.ColorPalette
24044  * @extends Roo.Component
24045  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24046  * Here's an example of typical usage:
24047  * <pre><code>
24048 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24049 cp.render('my-div');
24050
24051 cp.on('select', function(palette, selColor){
24052     // do something with selColor
24053 });
24054 </code></pre>
24055  * @constructor
24056  * Create a new ColorPalette
24057  * @param {Object} config The config object
24058  */
24059 Roo.ColorPalette = function(config){
24060     Roo.ColorPalette.superclass.constructor.call(this, config);
24061     this.addEvents({
24062         /**
24063              * @event select
24064              * Fires when a color is selected
24065              * @param {ColorPalette} this
24066              * @param {String} color The 6-digit color hex code (without the # symbol)
24067              */
24068         select: true
24069     });
24070
24071     if(this.handler){
24072         this.on("select", this.handler, this.scope, true);
24073     }
24074 };
24075 Roo.extend(Roo.ColorPalette, Roo.Component, {
24076     /**
24077      * @cfg {String} itemCls
24078      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24079      */
24080     itemCls : "x-color-palette",
24081     /**
24082      * @cfg {String} value
24083      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24084      * the hex codes are case-sensitive.
24085      */
24086     value : null,
24087     clickEvent:'click',
24088     // private
24089     ctype: "Roo.ColorPalette",
24090
24091     /**
24092      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24093      */
24094     allowReselect : false,
24095
24096     /**
24097      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24098      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24099      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24100      * of colors with the width setting until the box is symmetrical.</p>
24101      * <p>You can override individual colors if needed:</p>
24102      * <pre><code>
24103 var cp = new Roo.ColorPalette();
24104 cp.colors[0] = "FF0000";  // change the first box to red
24105 </code></pre>
24106
24107 Or you can provide a custom array of your own for complete control:
24108 <pre><code>
24109 var cp = new Roo.ColorPalette();
24110 cp.colors = ["000000", "993300", "333300"];
24111 </code></pre>
24112      * @type Array
24113      */
24114     colors : [
24115         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24116         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24117         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24118         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24119         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24120     ],
24121
24122     // private
24123     onRender : function(container, position){
24124         var t = new Roo.MasterTemplate(
24125             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24126         );
24127         var c = this.colors;
24128         for(var i = 0, len = c.length; i < len; i++){
24129             t.add([c[i]]);
24130         }
24131         var el = document.createElement("div");
24132         el.className = this.itemCls;
24133         t.overwrite(el);
24134         container.dom.insertBefore(el, position);
24135         this.el = Roo.get(el);
24136         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24137         if(this.clickEvent != 'click'){
24138             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24139         }
24140     },
24141
24142     // private
24143     afterRender : function(){
24144         Roo.ColorPalette.superclass.afterRender.call(this);
24145         if(this.value){
24146             var s = this.value;
24147             this.value = null;
24148             this.select(s);
24149         }
24150     },
24151
24152     // private
24153     handleClick : function(e, t){
24154         e.preventDefault();
24155         if(!this.disabled){
24156             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24157             this.select(c.toUpperCase());
24158         }
24159     },
24160
24161     /**
24162      * Selects the specified color in the palette (fires the select event)
24163      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24164      */
24165     select : function(color){
24166         color = color.replace("#", "");
24167         if(color != this.value || this.allowReselect){
24168             var el = this.el;
24169             if(this.value){
24170                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24171             }
24172             el.child("a.color-"+color).addClass("x-color-palette-sel");
24173             this.value = color;
24174             this.fireEvent("select", this, color);
24175         }
24176     }
24177 });/*
24178  * Based on:
24179  * Ext JS Library 1.1.1
24180  * Copyright(c) 2006-2007, Ext JS, LLC.
24181  *
24182  * Originally Released Under LGPL - original licence link has changed is not relivant.
24183  *
24184  * Fork - LGPL
24185  * <script type="text/javascript">
24186  */
24187  
24188 /**
24189  * @class Roo.DatePicker
24190  * @extends Roo.Component
24191  * Simple date picker class.
24192  * @constructor
24193  * Create a new DatePicker
24194  * @param {Object} config The config object
24195  */
24196 Roo.DatePicker = function(config){
24197     Roo.DatePicker.superclass.constructor.call(this, config);
24198
24199     this.value = config && config.value ?
24200                  config.value.clearTime() : new Date().clearTime();
24201
24202     this.addEvents({
24203         /**
24204              * @event select
24205              * Fires when a date is selected
24206              * @param {DatePicker} this
24207              * @param {Date} date The selected date
24208              */
24209         select: true
24210     });
24211
24212     if(this.handler){
24213         this.on("select", this.handler,  this.scope || this);
24214     }
24215     // build the disabledDatesRE
24216     if(!this.disabledDatesRE && this.disabledDates){
24217         var dd = this.disabledDates;
24218         var re = "(?:";
24219         for(var i = 0; i < dd.length; i++){
24220             re += dd[i];
24221             if(i != dd.length-1) re += "|";
24222         }
24223         this.disabledDatesRE = new RegExp(re + ")");
24224     }
24225 };
24226
24227 Roo.extend(Roo.DatePicker, Roo.Component, {
24228     /**
24229      * @cfg {String} todayText
24230      * The text to display on the button that selects the current date (defaults to "Today")
24231      */
24232     todayText : "Today",
24233     /**
24234      * @cfg {String} okText
24235      * The text to display on the ok button
24236      */
24237     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24238     /**
24239      * @cfg {String} cancelText
24240      * The text to display on the cancel button
24241      */
24242     cancelText : "Cancel",
24243     /**
24244      * @cfg {String} todayTip
24245      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24246      */
24247     todayTip : "{0} (Spacebar)",
24248     /**
24249      * @cfg {Date} minDate
24250      * Minimum allowable date (JavaScript date object, defaults to null)
24251      */
24252     minDate : null,
24253     /**
24254      * @cfg {Date} maxDate
24255      * Maximum allowable date (JavaScript date object, defaults to null)
24256      */
24257     maxDate : null,
24258     /**
24259      * @cfg {String} minText
24260      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24261      */
24262     minText : "This date is before the minimum date",
24263     /**
24264      * @cfg {String} maxText
24265      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24266      */
24267     maxText : "This date is after the maximum date",
24268     /**
24269      * @cfg {String} format
24270      * The default date format string which can be overriden for localization support.  The format must be
24271      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24272      */
24273     format : "m/d/y",
24274     /**
24275      * @cfg {Array} disabledDays
24276      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24277      */
24278     disabledDays : null,
24279     /**
24280      * @cfg {String} disabledDaysText
24281      * The tooltip to display when the date falls on a disabled day (defaults to "")
24282      */
24283     disabledDaysText : "",
24284     /**
24285      * @cfg {RegExp} disabledDatesRE
24286      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24287      */
24288     disabledDatesRE : null,
24289     /**
24290      * @cfg {String} disabledDatesText
24291      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24292      */
24293     disabledDatesText : "",
24294     /**
24295      * @cfg {Boolean} constrainToViewport
24296      * True to constrain the date picker to the viewport (defaults to true)
24297      */
24298     constrainToViewport : true,
24299     /**
24300      * @cfg {Array} monthNames
24301      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24302      */
24303     monthNames : Date.monthNames,
24304     /**
24305      * @cfg {Array} dayNames
24306      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24307      */
24308     dayNames : Date.dayNames,
24309     /**
24310      * @cfg {String} nextText
24311      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24312      */
24313     nextText: 'Next Month (Control+Right)',
24314     /**
24315      * @cfg {String} prevText
24316      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24317      */
24318     prevText: 'Previous Month (Control+Left)',
24319     /**
24320      * @cfg {String} monthYearText
24321      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24322      */
24323     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24324     /**
24325      * @cfg {Number} startDay
24326      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24327      */
24328     startDay : 0,
24329     /**
24330      * @cfg {Bool} showClear
24331      * Show a clear button (usefull for date form elements that can be blank.)
24332      */
24333     
24334     showClear: false,
24335     
24336     /**
24337      * Sets the value of the date field
24338      * @param {Date} value The date to set
24339      */
24340     setValue : function(value){
24341         var old = this.value;
24342         this.value = value.clearTime(true);
24343         if(this.el){
24344             this.update(this.value);
24345         }
24346     },
24347
24348     /**
24349      * Gets the current selected value of the date field
24350      * @return {Date} The selected date
24351      */
24352     getValue : function(){
24353         return this.value;
24354     },
24355
24356     // private
24357     focus : function(){
24358         if(this.el){
24359             this.update(this.activeDate);
24360         }
24361     },
24362
24363     // private
24364     onRender : function(container, position){
24365         var m = [
24366              '<table cellspacing="0">',
24367                 '<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>',
24368                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24369         var dn = this.dayNames;
24370         for(var i = 0; i < 7; i++){
24371             var d = this.startDay+i;
24372             if(d > 6){
24373                 d = d-7;
24374             }
24375             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24376         }
24377         m[m.length] = "</tr></thead><tbody><tr>";
24378         for(var i = 0; i < 42; i++) {
24379             if(i % 7 == 0 && i != 0){
24380                 m[m.length] = "</tr><tr>";
24381             }
24382             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24383         }
24384         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24385             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24386
24387         var el = document.createElement("div");
24388         el.className = "x-date-picker";
24389         el.innerHTML = m.join("");
24390
24391         container.dom.insertBefore(el, position);
24392
24393         this.el = Roo.get(el);
24394         this.eventEl = Roo.get(el.firstChild);
24395
24396         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24397             handler: this.showPrevMonth,
24398             scope: this,
24399             preventDefault:true,
24400             stopDefault:true
24401         });
24402
24403         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24404             handler: this.showNextMonth,
24405             scope: this,
24406             preventDefault:true,
24407             stopDefault:true
24408         });
24409
24410         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24411
24412         this.monthPicker = this.el.down('div.x-date-mp');
24413         this.monthPicker.enableDisplayMode('block');
24414         
24415         var kn = new Roo.KeyNav(this.eventEl, {
24416             "left" : function(e){
24417                 e.ctrlKey ?
24418                     this.showPrevMonth() :
24419                     this.update(this.activeDate.add("d", -1));
24420             },
24421
24422             "right" : function(e){
24423                 e.ctrlKey ?
24424                     this.showNextMonth() :
24425                     this.update(this.activeDate.add("d", 1));
24426             },
24427
24428             "up" : function(e){
24429                 e.ctrlKey ?
24430                     this.showNextYear() :
24431                     this.update(this.activeDate.add("d", -7));
24432             },
24433
24434             "down" : function(e){
24435                 e.ctrlKey ?
24436                     this.showPrevYear() :
24437                     this.update(this.activeDate.add("d", 7));
24438             },
24439
24440             "pageUp" : function(e){
24441                 this.showNextMonth();
24442             },
24443
24444             "pageDown" : function(e){
24445                 this.showPrevMonth();
24446             },
24447
24448             "enter" : function(e){
24449                 e.stopPropagation();
24450                 return true;
24451             },
24452
24453             scope : this
24454         });
24455
24456         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24457
24458         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24459
24460         this.el.unselectable();
24461         
24462         this.cells = this.el.select("table.x-date-inner tbody td");
24463         this.textNodes = this.el.query("table.x-date-inner tbody span");
24464
24465         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24466             text: "&#160;",
24467             tooltip: this.monthYearText
24468         });
24469
24470         this.mbtn.on('click', this.showMonthPicker, this);
24471         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24472
24473
24474         var today = (new Date()).dateFormat(this.format);
24475         
24476         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24477         baseTb.add({
24478             text: String.format(this.todayText, today),
24479             tooltip: String.format(this.todayTip, today),
24480             handler: this.selectToday,
24481             scope: this
24482         });
24483         
24484         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24485             
24486         //});
24487         if (this.showClear) {
24488             
24489             baseTb.add( new Roo.Toolbar.Fill());
24490             baseTb.add({
24491                 text: '&#160;',
24492                 cls: 'x-btn-icon x-btn-clear',
24493                 handler: function() {
24494                     //this.value = '';
24495                     this.fireEvent("select", this, '');
24496                 },
24497                 scope: this
24498             });
24499         }
24500         
24501         
24502         if(Roo.isIE){
24503             this.el.repaint();
24504         }
24505         this.update(this.value);
24506     },
24507
24508     createMonthPicker : function(){
24509         if(!this.monthPicker.dom.firstChild){
24510             var buf = ['<table border="0" cellspacing="0">'];
24511             for(var i = 0; i < 6; i++){
24512                 buf.push(
24513                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24514                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24515                     i == 0 ?
24516                     '<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>' :
24517                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24518                 );
24519             }
24520             buf.push(
24521                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24522                     this.okText,
24523                     '</button><button type="button" class="x-date-mp-cancel">',
24524                     this.cancelText,
24525                     '</button></td></tr>',
24526                 '</table>'
24527             );
24528             this.monthPicker.update(buf.join(''));
24529             this.monthPicker.on('click', this.onMonthClick, this);
24530             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24531
24532             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24533             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24534
24535             this.mpMonths.each(function(m, a, i){
24536                 i += 1;
24537                 if((i%2) == 0){
24538                     m.dom.xmonth = 5 + Math.round(i * .5);
24539                 }else{
24540                     m.dom.xmonth = Math.round((i-1) * .5);
24541                 }
24542             });
24543         }
24544     },
24545
24546     showMonthPicker : function(){
24547         this.createMonthPicker();
24548         var size = this.el.getSize();
24549         this.monthPicker.setSize(size);
24550         this.monthPicker.child('table').setSize(size);
24551
24552         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24553         this.updateMPMonth(this.mpSelMonth);
24554         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24555         this.updateMPYear(this.mpSelYear);
24556
24557         this.monthPicker.slideIn('t', {duration:.2});
24558     },
24559
24560     updateMPYear : function(y){
24561         this.mpyear = y;
24562         var ys = this.mpYears.elements;
24563         for(var i = 1; i <= 10; i++){
24564             var td = ys[i-1], y2;
24565             if((i%2) == 0){
24566                 y2 = y + Math.round(i * .5);
24567                 td.firstChild.innerHTML = y2;
24568                 td.xyear = y2;
24569             }else{
24570                 y2 = y - (5-Math.round(i * .5));
24571                 td.firstChild.innerHTML = y2;
24572                 td.xyear = y2;
24573             }
24574             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24575         }
24576     },
24577
24578     updateMPMonth : function(sm){
24579         this.mpMonths.each(function(m, a, i){
24580             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24581         });
24582     },
24583
24584     selectMPMonth: function(m){
24585         
24586     },
24587
24588     onMonthClick : function(e, t){
24589         e.stopEvent();
24590         var el = new Roo.Element(t), pn;
24591         if(el.is('button.x-date-mp-cancel')){
24592             this.hideMonthPicker();
24593         }
24594         else if(el.is('button.x-date-mp-ok')){
24595             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24596             this.hideMonthPicker();
24597         }
24598         else if(pn = el.up('td.x-date-mp-month', 2)){
24599             this.mpMonths.removeClass('x-date-mp-sel');
24600             pn.addClass('x-date-mp-sel');
24601             this.mpSelMonth = pn.dom.xmonth;
24602         }
24603         else if(pn = el.up('td.x-date-mp-year', 2)){
24604             this.mpYears.removeClass('x-date-mp-sel');
24605             pn.addClass('x-date-mp-sel');
24606             this.mpSelYear = pn.dom.xyear;
24607         }
24608         else if(el.is('a.x-date-mp-prev')){
24609             this.updateMPYear(this.mpyear-10);
24610         }
24611         else if(el.is('a.x-date-mp-next')){
24612             this.updateMPYear(this.mpyear+10);
24613         }
24614     },
24615
24616     onMonthDblClick : function(e, t){
24617         e.stopEvent();
24618         var el = new Roo.Element(t), pn;
24619         if(pn = el.up('td.x-date-mp-month', 2)){
24620             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24621             this.hideMonthPicker();
24622         }
24623         else if(pn = el.up('td.x-date-mp-year', 2)){
24624             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24625             this.hideMonthPicker();
24626         }
24627     },
24628
24629     hideMonthPicker : function(disableAnim){
24630         if(this.monthPicker){
24631             if(disableAnim === true){
24632                 this.monthPicker.hide();
24633             }else{
24634                 this.monthPicker.slideOut('t', {duration:.2});
24635             }
24636         }
24637     },
24638
24639     // private
24640     showPrevMonth : function(e){
24641         this.update(this.activeDate.add("mo", -1));
24642     },
24643
24644     // private
24645     showNextMonth : function(e){
24646         this.update(this.activeDate.add("mo", 1));
24647     },
24648
24649     // private
24650     showPrevYear : function(){
24651         this.update(this.activeDate.add("y", -1));
24652     },
24653
24654     // private
24655     showNextYear : function(){
24656         this.update(this.activeDate.add("y", 1));
24657     },
24658
24659     // private
24660     handleMouseWheel : function(e){
24661         var delta = e.getWheelDelta();
24662         if(delta > 0){
24663             this.showPrevMonth();
24664             e.stopEvent();
24665         } else if(delta < 0){
24666             this.showNextMonth();
24667             e.stopEvent();
24668         }
24669     },
24670
24671     // private
24672     handleDateClick : function(e, t){
24673         e.stopEvent();
24674         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24675             this.setValue(new Date(t.dateValue));
24676             this.fireEvent("select", this, this.value);
24677         }
24678     },
24679
24680     // private
24681     selectToday : function(){
24682         this.setValue(new Date().clearTime());
24683         this.fireEvent("select", this, this.value);
24684     },
24685
24686     // private
24687     update : function(date){
24688         var vd = this.activeDate;
24689         this.activeDate = date;
24690         if(vd && this.el){
24691             var t = date.getTime();
24692             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24693                 this.cells.removeClass("x-date-selected");
24694                 this.cells.each(function(c){
24695                    if(c.dom.firstChild.dateValue == t){
24696                        c.addClass("x-date-selected");
24697                        setTimeout(function(){
24698                             try{c.dom.firstChild.focus();}catch(e){}
24699                        }, 50);
24700                        return false;
24701                    }
24702                 });
24703                 return;
24704             }
24705         }
24706         var days = date.getDaysInMonth();
24707         var firstOfMonth = date.getFirstDateOfMonth();
24708         var startingPos = firstOfMonth.getDay()-this.startDay;
24709
24710         if(startingPos <= this.startDay){
24711             startingPos += 7;
24712         }
24713
24714         var pm = date.add("mo", -1);
24715         var prevStart = pm.getDaysInMonth()-startingPos;
24716
24717         var cells = this.cells.elements;
24718         var textEls = this.textNodes;
24719         days += startingPos;
24720
24721         // convert everything to numbers so it's fast
24722         var day = 86400000;
24723         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24724         var today = new Date().clearTime().getTime();
24725         var sel = date.clearTime().getTime();
24726         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24727         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24728         var ddMatch = this.disabledDatesRE;
24729         var ddText = this.disabledDatesText;
24730         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24731         var ddaysText = this.disabledDaysText;
24732         var format = this.format;
24733
24734         var setCellClass = function(cal, cell){
24735             cell.title = "";
24736             var t = d.getTime();
24737             cell.firstChild.dateValue = t;
24738             if(t == today){
24739                 cell.className += " x-date-today";
24740                 cell.title = cal.todayText;
24741             }
24742             if(t == sel){
24743                 cell.className += " x-date-selected";
24744                 setTimeout(function(){
24745                     try{cell.firstChild.focus();}catch(e){}
24746                 }, 50);
24747             }
24748             // disabling
24749             if(t < min) {
24750                 cell.className = " x-date-disabled";
24751                 cell.title = cal.minText;
24752                 return;
24753             }
24754             if(t > max) {
24755                 cell.className = " x-date-disabled";
24756                 cell.title = cal.maxText;
24757                 return;
24758             }
24759             if(ddays){
24760                 if(ddays.indexOf(d.getDay()) != -1){
24761                     cell.title = ddaysText;
24762                     cell.className = " x-date-disabled";
24763                 }
24764             }
24765             if(ddMatch && format){
24766                 var fvalue = d.dateFormat(format);
24767                 if(ddMatch.test(fvalue)){
24768                     cell.title = ddText.replace("%0", fvalue);
24769                     cell.className = " x-date-disabled";
24770                 }
24771             }
24772         };
24773
24774         var i = 0;
24775         for(; i < startingPos; i++) {
24776             textEls[i].innerHTML = (++prevStart);
24777             d.setDate(d.getDate()+1);
24778             cells[i].className = "x-date-prevday";
24779             setCellClass(this, cells[i]);
24780         }
24781         for(; i < days; i++){
24782             intDay = i - startingPos + 1;
24783             textEls[i].innerHTML = (intDay);
24784             d.setDate(d.getDate()+1);
24785             cells[i].className = "x-date-active";
24786             setCellClass(this, cells[i]);
24787         }
24788         var extraDays = 0;
24789         for(; i < 42; i++) {
24790              textEls[i].innerHTML = (++extraDays);
24791              d.setDate(d.getDate()+1);
24792              cells[i].className = "x-date-nextday";
24793              setCellClass(this, cells[i]);
24794         }
24795
24796         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24797
24798         if(!this.internalRender){
24799             var main = this.el.dom.firstChild;
24800             var w = main.offsetWidth;
24801             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24802             Roo.fly(main).setWidth(w);
24803             this.internalRender = true;
24804             // opera does not respect the auto grow header center column
24805             // then, after it gets a width opera refuses to recalculate
24806             // without a second pass
24807             if(Roo.isOpera && !this.secondPass){
24808                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24809                 this.secondPass = true;
24810                 this.update.defer(10, this, [date]);
24811             }
24812         }
24813     }
24814 });/*
24815  * Based on:
24816  * Ext JS Library 1.1.1
24817  * Copyright(c) 2006-2007, Ext JS, LLC.
24818  *
24819  * Originally Released Under LGPL - original licence link has changed is not relivant.
24820  *
24821  * Fork - LGPL
24822  * <script type="text/javascript">
24823  */
24824 /**
24825  * @class Roo.TabPanel
24826  * @extends Roo.util.Observable
24827  * A lightweight tab container.
24828  * <br><br>
24829  * Usage:
24830  * <pre><code>
24831 // basic tabs 1, built from existing content
24832 var tabs = new Roo.TabPanel("tabs1");
24833 tabs.addTab("script", "View Script");
24834 tabs.addTab("markup", "View Markup");
24835 tabs.activate("script");
24836
24837 // more advanced tabs, built from javascript
24838 var jtabs = new Roo.TabPanel("jtabs");
24839 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24840
24841 // set up the UpdateManager
24842 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24843 var updater = tab2.getUpdateManager();
24844 updater.setDefaultUrl("ajax1.htm");
24845 tab2.on('activate', updater.refresh, updater, true);
24846
24847 // Use setUrl for Ajax loading
24848 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24849 tab3.setUrl("ajax2.htm", null, true);
24850
24851 // Disabled tab
24852 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24853 tab4.disable();
24854
24855 jtabs.activate("jtabs-1");
24856  * </code></pre>
24857  * @constructor
24858  * Create a new TabPanel.
24859  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24860  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24861  */
24862 Roo.TabPanel = function(container, config){
24863     /**
24864     * The container element for this TabPanel.
24865     * @type Roo.Element
24866     */
24867     this.el = Roo.get(container, true);
24868     if(config){
24869         if(typeof config == "boolean"){
24870             this.tabPosition = config ? "bottom" : "top";
24871         }else{
24872             Roo.apply(this, config);
24873         }
24874     }
24875     if(this.tabPosition == "bottom"){
24876         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24877         this.el.addClass("x-tabs-bottom");
24878     }
24879     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24880     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24881     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24882     if(Roo.isIE){
24883         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24884     }
24885     if(this.tabPosition != "bottom"){
24886     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24887      * @type Roo.Element
24888      */
24889       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24890       this.el.addClass("x-tabs-top");
24891     }
24892     this.items = [];
24893
24894     this.bodyEl.setStyle("position", "relative");
24895
24896     this.active = null;
24897     this.activateDelegate = this.activate.createDelegate(this);
24898
24899     this.addEvents({
24900         /**
24901          * @event tabchange
24902          * Fires when the active tab changes
24903          * @param {Roo.TabPanel} this
24904          * @param {Roo.TabPanelItem} activePanel The new active tab
24905          */
24906         "tabchange": true,
24907         /**
24908          * @event beforetabchange
24909          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24910          * @param {Roo.TabPanel} this
24911          * @param {Object} e Set cancel to true on this object to cancel the tab change
24912          * @param {Roo.TabPanelItem} tab The tab being changed to
24913          */
24914         "beforetabchange" : true
24915     });
24916
24917     Roo.EventManager.onWindowResize(this.onResize, this);
24918     this.cpad = this.el.getPadding("lr");
24919     this.hiddenCount = 0;
24920
24921     Roo.TabPanel.superclass.constructor.call(this);
24922 };
24923
24924 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24925         /*
24926          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24927          */
24928     tabPosition : "top",
24929         /*
24930          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24931          */
24932     currentTabWidth : 0,
24933         /*
24934          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24935          */
24936     minTabWidth : 40,
24937         /*
24938          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24939          */
24940     maxTabWidth : 250,
24941         /*
24942          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24943          */
24944     preferredTabWidth : 175,
24945         /*
24946          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24947          */
24948     resizeTabs : false,
24949         /*
24950          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24951          */
24952     monitorResize : true,
24953
24954     /**
24955      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24956      * @param {String} id The id of the div to use <b>or create</b>
24957      * @param {String} text The text for the tab
24958      * @param {String} content (optional) Content to put in the TabPanelItem body
24959      * @param {Boolean} closable (optional) True to create a close icon on the tab
24960      * @return {Roo.TabPanelItem} The created TabPanelItem
24961      */
24962     addTab : function(id, text, content, closable){
24963         var item = new Roo.TabPanelItem(this, id, text, closable);
24964         this.addTabItem(item);
24965         if(content){
24966             item.setContent(content);
24967         }
24968         return item;
24969     },
24970
24971     /**
24972      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24973      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24974      * @return {Roo.TabPanelItem}
24975      */
24976     getTab : function(id){
24977         return this.items[id];
24978     },
24979
24980     /**
24981      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24982      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24983      */
24984     hideTab : function(id){
24985         var t = this.items[id];
24986         if(!t.isHidden()){
24987            t.setHidden(true);
24988            this.hiddenCount++;
24989            this.autoSizeTabs();
24990         }
24991     },
24992
24993     /**
24994      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
24995      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
24996      */
24997     unhideTab : function(id){
24998         var t = this.items[id];
24999         if(t.isHidden()){
25000            t.setHidden(false);
25001            this.hiddenCount--;
25002            this.autoSizeTabs();
25003         }
25004     },
25005
25006     /**
25007      * Adds an existing {@link Roo.TabPanelItem}.
25008      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25009      */
25010     addTabItem : function(item){
25011         this.items[item.id] = item;
25012         this.items.push(item);
25013         if(this.resizeTabs){
25014            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25015            this.autoSizeTabs();
25016         }else{
25017             item.autoSize();
25018         }
25019     },
25020
25021     /**
25022      * Removes a {@link Roo.TabPanelItem}.
25023      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25024      */
25025     removeTab : function(id){
25026         var items = this.items;
25027         var tab = items[id];
25028         if(!tab) return;
25029         var index = items.indexOf(tab);
25030         if(this.active == tab && items.length > 1){
25031             var newTab = this.getNextAvailable(index);
25032             if(newTab)newTab.activate();
25033         }
25034         this.stripEl.dom.removeChild(tab.pnode.dom);
25035         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25036             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25037         }
25038         items.splice(index, 1);
25039         delete this.items[tab.id];
25040         tab.fireEvent("close", tab);
25041         tab.purgeListeners();
25042         this.autoSizeTabs();
25043     },
25044
25045     getNextAvailable : function(start){
25046         var items = this.items;
25047         var index = start;
25048         // look for a next tab that will slide over to
25049         // replace the one being removed
25050         while(index < items.length){
25051             var item = items[++index];
25052             if(item && !item.isHidden()){
25053                 return item;
25054             }
25055         }
25056         // if one isn't found select the previous tab (on the left)
25057         index = start;
25058         while(index >= 0){
25059             var item = items[--index];
25060             if(item && !item.isHidden()){
25061                 return item;
25062             }
25063         }
25064         return null;
25065     },
25066
25067     /**
25068      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25069      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25070      */
25071     disableTab : function(id){
25072         var tab = this.items[id];
25073         if(tab && this.active != tab){
25074             tab.disable();
25075         }
25076     },
25077
25078     /**
25079      * Enables a {@link Roo.TabPanelItem} that is disabled.
25080      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25081      */
25082     enableTab : function(id){
25083         var tab = this.items[id];
25084         tab.enable();
25085     },
25086
25087     /**
25088      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25089      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25090      * @return {Roo.TabPanelItem} The TabPanelItem.
25091      */
25092     activate : function(id){
25093         var tab = this.items[id];
25094         if(!tab){
25095             return null;
25096         }
25097         if(tab == this.active || tab.disabled){
25098             return tab;
25099         }
25100         var e = {};
25101         this.fireEvent("beforetabchange", this, e, tab);
25102         if(e.cancel !== true && !tab.disabled){
25103             if(this.active){
25104                 this.active.hide();
25105             }
25106             this.active = this.items[id];
25107             this.active.show();
25108             this.fireEvent("tabchange", this, this.active);
25109         }
25110         return tab;
25111     },
25112
25113     /**
25114      * Gets the active {@link Roo.TabPanelItem}.
25115      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25116      */
25117     getActiveTab : function(){
25118         return this.active;
25119     },
25120
25121     /**
25122      * Updates the tab body element to fit the height of the container element
25123      * for overflow scrolling
25124      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25125      */
25126     syncHeight : function(targetHeight){
25127         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25128         var bm = this.bodyEl.getMargins();
25129         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25130         this.bodyEl.setHeight(newHeight);
25131         return newHeight;
25132     },
25133
25134     onResize : function(){
25135         if(this.monitorResize){
25136             this.autoSizeTabs();
25137         }
25138     },
25139
25140     /**
25141      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25142      */
25143     beginUpdate : function(){
25144         this.updating = true;
25145     },
25146
25147     /**
25148      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25149      */
25150     endUpdate : function(){
25151         this.updating = false;
25152         this.autoSizeTabs();
25153     },
25154
25155     /**
25156      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25157      */
25158     autoSizeTabs : function(){
25159         var count = this.items.length;
25160         var vcount = count - this.hiddenCount;
25161         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25162         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25163         var availWidth = Math.floor(w / vcount);
25164         var b = this.stripBody;
25165         if(b.getWidth() > w){
25166             var tabs = this.items;
25167             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25168             if(availWidth < this.minTabWidth){
25169                 /*if(!this.sleft){    // incomplete scrolling code
25170                     this.createScrollButtons();
25171                 }
25172                 this.showScroll();
25173                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25174             }
25175         }else{
25176             if(this.currentTabWidth < this.preferredTabWidth){
25177                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25178             }
25179         }
25180     },
25181
25182     /**
25183      * Returns the number of tabs in this TabPanel.
25184      * @return {Number}
25185      */
25186      getCount : function(){
25187          return this.items.length;
25188      },
25189
25190     /**
25191      * Resizes all the tabs to the passed width
25192      * @param {Number} The new width
25193      */
25194     setTabWidth : function(width){
25195         this.currentTabWidth = width;
25196         for(var i = 0, len = this.items.length; i < len; i++) {
25197                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25198         }
25199     },
25200
25201     /**
25202      * Destroys this TabPanel
25203      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25204      */
25205     destroy : function(removeEl){
25206         Roo.EventManager.removeResizeListener(this.onResize, this);
25207         for(var i = 0, len = this.items.length; i < len; i++){
25208             this.items[i].purgeListeners();
25209         }
25210         if(removeEl === true){
25211             this.el.update("");
25212             this.el.remove();
25213         }
25214     }
25215 });
25216
25217 /**
25218  * @class Roo.TabPanelItem
25219  * @extends Roo.util.Observable
25220  * Represents an individual item (tab plus body) in a TabPanel.
25221  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25222  * @param {String} id The id of this TabPanelItem
25223  * @param {String} text The text for the tab of this TabPanelItem
25224  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25225  */
25226 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25227     /**
25228      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25229      * @type Roo.TabPanel
25230      */
25231     this.tabPanel = tabPanel;
25232     /**
25233      * The id for this TabPanelItem
25234      * @type String
25235      */
25236     this.id = id;
25237     /** @private */
25238     this.disabled = false;
25239     /** @private */
25240     this.text = text;
25241     /** @private */
25242     this.loaded = false;
25243     this.closable = closable;
25244
25245     /**
25246      * The body element for this TabPanelItem.
25247      * @type Roo.Element
25248      */
25249     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25250     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25251     this.bodyEl.setStyle("display", "block");
25252     this.bodyEl.setStyle("zoom", "1");
25253     this.hideAction();
25254
25255     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25256     /** @private */
25257     this.el = Roo.get(els.el, true);
25258     this.inner = Roo.get(els.inner, true);
25259     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25260     this.pnode = Roo.get(els.el.parentNode, true);
25261     this.el.on("mousedown", this.onTabMouseDown, this);
25262     this.el.on("click", this.onTabClick, this);
25263     /** @private */
25264     if(closable){
25265         var c = Roo.get(els.close, true);
25266         c.dom.title = this.closeText;
25267         c.addClassOnOver("close-over");
25268         c.on("click", this.closeClick, this);
25269      }
25270
25271     this.addEvents({
25272          /**
25273          * @event activate
25274          * Fires when this tab becomes the active tab.
25275          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25276          * @param {Roo.TabPanelItem} this
25277          */
25278         "activate": true,
25279         /**
25280          * @event beforeclose
25281          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25282          * @param {Roo.TabPanelItem} this
25283          * @param {Object} e Set cancel to true on this object to cancel the close.
25284          */
25285         "beforeclose": true,
25286         /**
25287          * @event close
25288          * Fires when this tab is closed.
25289          * @param {Roo.TabPanelItem} this
25290          */
25291          "close": true,
25292         /**
25293          * @event deactivate
25294          * Fires when this tab is no longer the active tab.
25295          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25296          * @param {Roo.TabPanelItem} this
25297          */
25298          "deactivate" : true
25299     });
25300     this.hidden = false;
25301
25302     Roo.TabPanelItem.superclass.constructor.call(this);
25303 };
25304
25305 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25306     purgeListeners : function(){
25307        Roo.util.Observable.prototype.purgeListeners.call(this);
25308        this.el.removeAllListeners();
25309     },
25310     /**
25311      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25312      */
25313     show : function(){
25314         this.pnode.addClass("on");
25315         this.showAction();
25316         if(Roo.isOpera){
25317             this.tabPanel.stripWrap.repaint();
25318         }
25319         this.fireEvent("activate", this.tabPanel, this);
25320     },
25321
25322     /**
25323      * Returns true if this tab is the active tab.
25324      * @return {Boolean}
25325      */
25326     isActive : function(){
25327         return this.tabPanel.getActiveTab() == this;
25328     },
25329
25330     /**
25331      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25332      */
25333     hide : function(){
25334         this.pnode.removeClass("on");
25335         this.hideAction();
25336         this.fireEvent("deactivate", this.tabPanel, this);
25337     },
25338
25339     hideAction : function(){
25340         this.bodyEl.hide();
25341         this.bodyEl.setStyle("position", "absolute");
25342         this.bodyEl.setLeft("-20000px");
25343         this.bodyEl.setTop("-20000px");
25344     },
25345
25346     showAction : function(){
25347         this.bodyEl.setStyle("position", "relative");
25348         this.bodyEl.setTop("");
25349         this.bodyEl.setLeft("");
25350         this.bodyEl.show();
25351     },
25352
25353     /**
25354      * Set the tooltip for the tab.
25355      * @param {String} tooltip The tab's tooltip
25356      */
25357     setTooltip : function(text){
25358         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25359             this.textEl.dom.qtip = text;
25360             this.textEl.dom.removeAttribute('title');
25361         }else{
25362             this.textEl.dom.title = text;
25363         }
25364     },
25365
25366     onTabClick : function(e){
25367         e.preventDefault();
25368         this.tabPanel.activate(this.id);
25369     },
25370
25371     onTabMouseDown : function(e){
25372         e.preventDefault();
25373         this.tabPanel.activate(this.id);
25374     },
25375
25376     getWidth : function(){
25377         return this.inner.getWidth();
25378     },
25379
25380     setWidth : function(width){
25381         var iwidth = width - this.pnode.getPadding("lr");
25382         this.inner.setWidth(iwidth);
25383         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25384         this.pnode.setWidth(width);
25385     },
25386
25387     /**
25388      * Show or hide the tab
25389      * @param {Boolean} hidden True to hide or false to show.
25390      */
25391     setHidden : function(hidden){
25392         this.hidden = hidden;
25393         this.pnode.setStyle("display", hidden ? "none" : "");
25394     },
25395
25396     /**
25397      * Returns true if this tab is "hidden"
25398      * @return {Boolean}
25399      */
25400     isHidden : function(){
25401         return this.hidden;
25402     },
25403
25404     /**
25405      * Returns the text for this tab
25406      * @return {String}
25407      */
25408     getText : function(){
25409         return this.text;
25410     },
25411
25412     autoSize : function(){
25413         //this.el.beginMeasure();
25414         this.textEl.setWidth(1);
25415         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25416         //this.el.endMeasure();
25417     },
25418
25419     /**
25420      * Sets the text for the tab (Note: this also sets the tooltip text)
25421      * @param {String} text The tab's text and tooltip
25422      */
25423     setText : function(text){
25424         this.text = text;
25425         this.textEl.update(text);
25426         this.setTooltip(text);
25427         if(!this.tabPanel.resizeTabs){
25428             this.autoSize();
25429         }
25430     },
25431     /**
25432      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25433      */
25434     activate : function(){
25435         this.tabPanel.activate(this.id);
25436     },
25437
25438     /**
25439      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25440      */
25441     disable : function(){
25442         if(this.tabPanel.active != this){
25443             this.disabled = true;
25444             this.pnode.addClass("disabled");
25445         }
25446     },
25447
25448     /**
25449      * Enables this TabPanelItem if it was previously disabled.
25450      */
25451     enable : function(){
25452         this.disabled = false;
25453         this.pnode.removeClass("disabled");
25454     },
25455
25456     /**
25457      * Sets the content for this TabPanelItem.
25458      * @param {String} content The content
25459      * @param {Boolean} loadScripts true to look for and load scripts
25460      */
25461     setContent : function(content, loadScripts){
25462         this.bodyEl.update(content, loadScripts);
25463     },
25464
25465     /**
25466      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25467      * @return {Roo.UpdateManager} The UpdateManager
25468      */
25469     getUpdateManager : function(){
25470         return this.bodyEl.getUpdateManager();
25471     },
25472
25473     /**
25474      * Set a URL to be used to load the content for this TabPanelItem.
25475      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25476      * @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)
25477      * @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)
25478      * @return {Roo.UpdateManager} The UpdateManager
25479      */
25480     setUrl : function(url, params, loadOnce){
25481         if(this.refreshDelegate){
25482             this.un('activate', this.refreshDelegate);
25483         }
25484         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25485         this.on("activate", this.refreshDelegate);
25486         return this.bodyEl.getUpdateManager();
25487     },
25488
25489     /** @private */
25490     _handleRefresh : function(url, params, loadOnce){
25491         if(!loadOnce || !this.loaded){
25492             var updater = this.bodyEl.getUpdateManager();
25493             updater.update(url, params, this._setLoaded.createDelegate(this));
25494         }
25495     },
25496
25497     /**
25498      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25499      *   Will fail silently if the setUrl method has not been called.
25500      *   This does not activate the panel, just updates its content.
25501      */
25502     refresh : function(){
25503         if(this.refreshDelegate){
25504            this.loaded = false;
25505            this.refreshDelegate();
25506         }
25507     },
25508
25509     /** @private */
25510     _setLoaded : function(){
25511         this.loaded = true;
25512     },
25513
25514     /** @private */
25515     closeClick : function(e){
25516         var o = {};
25517         e.stopEvent();
25518         this.fireEvent("beforeclose", this, o);
25519         if(o.cancel !== true){
25520             this.tabPanel.removeTab(this.id);
25521         }
25522     },
25523     /**
25524      * The text displayed in the tooltip for the close icon.
25525      * @type String
25526      */
25527     closeText : "Close this tab"
25528 });
25529
25530 /** @private */
25531 Roo.TabPanel.prototype.createStrip = function(container){
25532     var strip = document.createElement("div");
25533     strip.className = "x-tabs-wrap";
25534     container.appendChild(strip);
25535     return strip;
25536 };
25537 /** @private */
25538 Roo.TabPanel.prototype.createStripList = function(strip){
25539     // div wrapper for retard IE
25540     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>';
25541     return strip.firstChild.firstChild.firstChild.firstChild;
25542 };
25543 /** @private */
25544 Roo.TabPanel.prototype.createBody = function(container){
25545     var body = document.createElement("div");
25546     Roo.id(body, "tab-body");
25547     Roo.fly(body).addClass("x-tabs-body");
25548     container.appendChild(body);
25549     return body;
25550 };
25551 /** @private */
25552 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25553     var body = Roo.getDom(id);
25554     if(!body){
25555         body = document.createElement("div");
25556         body.id = id;
25557     }
25558     Roo.fly(body).addClass("x-tabs-item-body");
25559     bodyEl.insertBefore(body, bodyEl.firstChild);
25560     return body;
25561 };
25562 /** @private */
25563 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25564     var td = document.createElement("td");
25565     stripEl.appendChild(td);
25566     if(closable){
25567         td.className = "x-tabs-closable";
25568         if(!this.closeTpl){
25569             this.closeTpl = new Roo.Template(
25570                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25571                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25572                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25573             );
25574         }
25575         var el = this.closeTpl.overwrite(td, {"text": text});
25576         var close = el.getElementsByTagName("div")[0];
25577         var inner = el.getElementsByTagName("em")[0];
25578         return {"el": el, "close": close, "inner": inner};
25579     } else {
25580         if(!this.tabTpl){
25581             this.tabTpl = new Roo.Template(
25582                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25583                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25584             );
25585         }
25586         var el = this.tabTpl.overwrite(td, {"text": text});
25587         var inner = el.getElementsByTagName("em")[0];
25588         return {"el": el, "inner": inner};
25589     }
25590 };/*
25591  * Based on:
25592  * Ext JS Library 1.1.1
25593  * Copyright(c) 2006-2007, Ext JS, LLC.
25594  *
25595  * Originally Released Under LGPL - original licence link has changed is not relivant.
25596  *
25597  * Fork - LGPL
25598  * <script type="text/javascript">
25599  */
25600
25601 /**
25602  * @class Roo.Button
25603  * @extends Roo.util.Observable
25604  * Simple Button class
25605  * @cfg {String} text The button text
25606  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25607  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25608  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25609  * @cfg {Object} scope The scope of the handler
25610  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25611  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25612  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25613  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25614  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25615  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25616    applies if enableToggle = true)
25617  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25618  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25619   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25620  * @constructor
25621  * Create a new button
25622  * @param {Object} config The config object
25623  */
25624 Roo.Button = function(renderTo, config)
25625 {
25626     if (!config) {
25627         config = renderTo;
25628         renderTo = config.renderTo || false;
25629     }
25630     
25631     Roo.apply(this, config);
25632     this.addEvents({
25633         /**
25634              * @event click
25635              * Fires when this button is clicked
25636              * @param {Button} this
25637              * @param {EventObject} e The click event
25638              */
25639             "click" : true,
25640         /**
25641              * @event toggle
25642              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25643              * @param {Button} this
25644              * @param {Boolean} pressed
25645              */
25646             "toggle" : true,
25647         /**
25648              * @event mouseover
25649              * Fires when the mouse hovers over the button
25650              * @param {Button} this
25651              * @param {Event} e The event object
25652              */
25653         'mouseover' : true,
25654         /**
25655              * @event mouseout
25656              * Fires when the mouse exits the button
25657              * @param {Button} this
25658              * @param {Event} e The event object
25659              */
25660         'mouseout': true,
25661          /**
25662              * @event render
25663              * Fires when the button is rendered
25664              * @param {Button} this
25665              */
25666         'render': true
25667     });
25668     if(this.menu){
25669         this.menu = Roo.menu.MenuMgr.get(this.menu);
25670     }
25671     if(renderTo){
25672         this.render(renderTo);
25673     }
25674     
25675     Roo.util.Observable.call(this);
25676 };
25677
25678 Roo.extend(Roo.Button, Roo.util.Observable, {
25679     /**
25680      * 
25681      */
25682     
25683     /**
25684      * Read-only. True if this button is hidden
25685      * @type Boolean
25686      */
25687     hidden : false,
25688     /**
25689      * Read-only. True if this button is disabled
25690      * @type Boolean
25691      */
25692     disabled : false,
25693     /**
25694      * Read-only. True if this button is pressed (only if enableToggle = true)
25695      * @type Boolean
25696      */
25697     pressed : false,
25698
25699     /**
25700      * @cfg {Number} tabIndex 
25701      * The DOM tabIndex for this button (defaults to undefined)
25702      */
25703     tabIndex : undefined,
25704
25705     /**
25706      * @cfg {Boolean} enableToggle
25707      * True to enable pressed/not pressed toggling (defaults to false)
25708      */
25709     enableToggle: false,
25710     /**
25711      * @cfg {Mixed} menu
25712      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25713      */
25714     menu : undefined,
25715     /**
25716      * @cfg {String} menuAlign
25717      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25718      */
25719     menuAlign : "tl-bl?",
25720
25721     /**
25722      * @cfg {String} iconCls
25723      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25724      */
25725     iconCls : undefined,
25726     /**
25727      * @cfg {String} type
25728      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25729      */
25730     type : 'button',
25731
25732     // private
25733     menuClassTarget: 'tr',
25734
25735     /**
25736      * @cfg {String} clickEvent
25737      * The type of event to map to the button's event handler (defaults to 'click')
25738      */
25739     clickEvent : 'click',
25740
25741     /**
25742      * @cfg {Boolean} handleMouseEvents
25743      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25744      */
25745     handleMouseEvents : true,
25746
25747     /**
25748      * @cfg {String} tooltipType
25749      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25750      */
25751     tooltipType : 'qtip',
25752
25753     /**
25754      * @cfg {String} cls
25755      * A CSS class to apply to the button's main element.
25756      */
25757     
25758     /**
25759      * @cfg {Roo.Template} template (Optional)
25760      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25761      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25762      * require code modifications if required elements (e.g. a button) aren't present.
25763      */
25764
25765     // private
25766     render : function(renderTo){
25767         var btn;
25768         if(this.hideParent){
25769             this.parentEl = Roo.get(renderTo);
25770         }
25771         if(!this.dhconfig){
25772             if(!this.template){
25773                 if(!Roo.Button.buttonTemplate){
25774                     // hideous table template
25775                     Roo.Button.buttonTemplate = new Roo.Template(
25776                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25777                         '<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>',
25778                         "</tr></tbody></table>");
25779                 }
25780                 this.template = Roo.Button.buttonTemplate;
25781             }
25782             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25783             var btnEl = btn.child("button:first");
25784             btnEl.on('focus', this.onFocus, this);
25785             btnEl.on('blur', this.onBlur, this);
25786             if(this.cls){
25787                 btn.addClass(this.cls);
25788             }
25789             if(this.icon){
25790                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25791             }
25792             if(this.iconCls){
25793                 btnEl.addClass(this.iconCls);
25794                 if(!this.cls){
25795                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25796                 }
25797             }
25798             if(this.tabIndex !== undefined){
25799                 btnEl.dom.tabIndex = this.tabIndex;
25800             }
25801             if(this.tooltip){
25802                 if(typeof this.tooltip == 'object'){
25803                     Roo.QuickTips.tips(Roo.apply({
25804                           target: btnEl.id
25805                     }, this.tooltip));
25806                 } else {
25807                     btnEl.dom[this.tooltipType] = this.tooltip;
25808                 }
25809             }
25810         }else{
25811             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25812         }
25813         this.el = btn;
25814         if(this.id){
25815             this.el.dom.id = this.el.id = this.id;
25816         }
25817         if(this.menu){
25818             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25819             this.menu.on("show", this.onMenuShow, this);
25820             this.menu.on("hide", this.onMenuHide, this);
25821         }
25822         btn.addClass("x-btn");
25823         if(Roo.isIE && !Roo.isIE7){
25824             this.autoWidth.defer(1, this);
25825         }else{
25826             this.autoWidth();
25827         }
25828         if(this.handleMouseEvents){
25829             btn.on("mouseover", this.onMouseOver, this);
25830             btn.on("mouseout", this.onMouseOut, this);
25831             btn.on("mousedown", this.onMouseDown, this);
25832         }
25833         btn.on(this.clickEvent, this.onClick, this);
25834         //btn.on("mouseup", this.onMouseUp, this);
25835         if(this.hidden){
25836             this.hide();
25837         }
25838         if(this.disabled){
25839             this.disable();
25840         }
25841         Roo.ButtonToggleMgr.register(this);
25842         if(this.pressed){
25843             this.el.addClass("x-btn-pressed");
25844         }
25845         if(this.repeat){
25846             var repeater = new Roo.util.ClickRepeater(btn,
25847                 typeof this.repeat == "object" ? this.repeat : {}
25848             );
25849             repeater.on("click", this.onClick,  this);
25850         }
25851         this.fireEvent('render', this);
25852         
25853     },
25854     /**
25855      * Returns the button's underlying element
25856      * @return {Roo.Element} The element
25857      */
25858     getEl : function(){
25859         return this.el;  
25860     },
25861     
25862     /**
25863      * Destroys this Button and removes any listeners.
25864      */
25865     destroy : function(){
25866         Roo.ButtonToggleMgr.unregister(this);
25867         this.el.removeAllListeners();
25868         this.purgeListeners();
25869         this.el.remove();
25870     },
25871
25872     // private
25873     autoWidth : function(){
25874         if(this.el){
25875             this.el.setWidth("auto");
25876             if(Roo.isIE7 && Roo.isStrict){
25877                 var ib = this.el.child('button');
25878                 if(ib && ib.getWidth() > 20){
25879                     ib.clip();
25880                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25881                 }
25882             }
25883             if(this.minWidth){
25884                 if(this.hidden){
25885                     this.el.beginMeasure();
25886                 }
25887                 if(this.el.getWidth() < this.minWidth){
25888                     this.el.setWidth(this.minWidth);
25889                 }
25890                 if(this.hidden){
25891                     this.el.endMeasure();
25892                 }
25893             }
25894         }
25895     },
25896
25897     /**
25898      * Assigns this button's click handler
25899      * @param {Function} handler The function to call when the button is clicked
25900      * @param {Object} scope (optional) Scope for the function passed in
25901      */
25902     setHandler : function(handler, scope){
25903         this.handler = handler;
25904         this.scope = scope;  
25905     },
25906     
25907     /**
25908      * Sets this button's text
25909      * @param {String} text The button text
25910      */
25911     setText : function(text){
25912         this.text = text;
25913         if(this.el){
25914             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25915         }
25916         this.autoWidth();
25917     },
25918     
25919     /**
25920      * Gets the text for this button
25921      * @return {String} The button text
25922      */
25923     getText : function(){
25924         return this.text;  
25925     },
25926     
25927     /**
25928      * Show this button
25929      */
25930     show: function(){
25931         this.hidden = false;
25932         if(this.el){
25933             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25934         }
25935     },
25936     
25937     /**
25938      * Hide this button
25939      */
25940     hide: function(){
25941         this.hidden = true;
25942         if(this.el){
25943             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25944         }
25945     },
25946     
25947     /**
25948      * Convenience function for boolean show/hide
25949      * @param {Boolean} visible True to show, false to hide
25950      */
25951     setVisible: function(visible){
25952         if(visible) {
25953             this.show();
25954         }else{
25955             this.hide();
25956         }
25957     },
25958     
25959     /**
25960      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25961      * @param {Boolean} state (optional) Force a particular state
25962      */
25963     toggle : function(state){
25964         state = state === undefined ? !this.pressed : state;
25965         if(state != this.pressed){
25966             if(state){
25967                 this.el.addClass("x-btn-pressed");
25968                 this.pressed = true;
25969                 this.fireEvent("toggle", this, true);
25970             }else{
25971                 this.el.removeClass("x-btn-pressed");
25972                 this.pressed = false;
25973                 this.fireEvent("toggle", this, false);
25974             }
25975             if(this.toggleHandler){
25976                 this.toggleHandler.call(this.scope || this, this, state);
25977             }
25978         }
25979     },
25980     
25981     /**
25982      * Focus the button
25983      */
25984     focus : function(){
25985         this.el.child('button:first').focus();
25986     },
25987     
25988     /**
25989      * Disable this button
25990      */
25991     disable : function(){
25992         if(this.el){
25993             this.el.addClass("x-btn-disabled");
25994         }
25995         this.disabled = true;
25996     },
25997     
25998     /**
25999      * Enable this button
26000      */
26001     enable : function(){
26002         if(this.el){
26003             this.el.removeClass("x-btn-disabled");
26004         }
26005         this.disabled = false;
26006     },
26007
26008     /**
26009      * Convenience function for boolean enable/disable
26010      * @param {Boolean} enabled True to enable, false to disable
26011      */
26012     setDisabled : function(v){
26013         this[v !== true ? "enable" : "disable"]();
26014     },
26015
26016     // private
26017     onClick : function(e){
26018         if(e){
26019             e.preventDefault();
26020         }
26021         if(e.button != 0){
26022             return;
26023         }
26024         if(!this.disabled){
26025             if(this.enableToggle){
26026                 this.toggle();
26027             }
26028             if(this.menu && !this.menu.isVisible()){
26029                 this.menu.show(this.el, this.menuAlign);
26030             }
26031             this.fireEvent("click", this, e);
26032             if(this.handler){
26033                 this.el.removeClass("x-btn-over");
26034                 this.handler.call(this.scope || this, this, e);
26035             }
26036         }
26037     },
26038     // private
26039     onMouseOver : function(e){
26040         if(!this.disabled){
26041             this.el.addClass("x-btn-over");
26042             this.fireEvent('mouseover', this, e);
26043         }
26044     },
26045     // private
26046     onMouseOut : function(e){
26047         if(!e.within(this.el,  true)){
26048             this.el.removeClass("x-btn-over");
26049             this.fireEvent('mouseout', this, e);
26050         }
26051     },
26052     // private
26053     onFocus : function(e){
26054         if(!this.disabled){
26055             this.el.addClass("x-btn-focus");
26056         }
26057     },
26058     // private
26059     onBlur : function(e){
26060         this.el.removeClass("x-btn-focus");
26061     },
26062     // private
26063     onMouseDown : function(e){
26064         if(!this.disabled && e.button == 0){
26065             this.el.addClass("x-btn-click");
26066             Roo.get(document).on('mouseup', this.onMouseUp, this);
26067         }
26068     },
26069     // private
26070     onMouseUp : function(e){
26071         if(e.button == 0){
26072             this.el.removeClass("x-btn-click");
26073             Roo.get(document).un('mouseup', this.onMouseUp, this);
26074         }
26075     },
26076     // private
26077     onMenuShow : function(e){
26078         this.el.addClass("x-btn-menu-active");
26079     },
26080     // private
26081     onMenuHide : function(e){
26082         this.el.removeClass("x-btn-menu-active");
26083     }   
26084 });
26085
26086 // Private utility class used by Button
26087 Roo.ButtonToggleMgr = function(){
26088    var groups = {};
26089    
26090    function toggleGroup(btn, state){
26091        if(state){
26092            var g = groups[btn.toggleGroup];
26093            for(var i = 0, l = g.length; i < l; i++){
26094                if(g[i] != btn){
26095                    g[i].toggle(false);
26096                }
26097            }
26098        }
26099    }
26100    
26101    return {
26102        register : function(btn){
26103            if(!btn.toggleGroup){
26104                return;
26105            }
26106            var g = groups[btn.toggleGroup];
26107            if(!g){
26108                g = groups[btn.toggleGroup] = [];
26109            }
26110            g.push(btn);
26111            btn.on("toggle", toggleGroup);
26112        },
26113        
26114        unregister : function(btn){
26115            if(!btn.toggleGroup){
26116                return;
26117            }
26118            var g = groups[btn.toggleGroup];
26119            if(g){
26120                g.remove(btn);
26121                btn.un("toggle", toggleGroup);
26122            }
26123        }
26124    };
26125 }();/*
26126  * Based on:
26127  * Ext JS Library 1.1.1
26128  * Copyright(c) 2006-2007, Ext JS, LLC.
26129  *
26130  * Originally Released Under LGPL - original licence link has changed is not relivant.
26131  *
26132  * Fork - LGPL
26133  * <script type="text/javascript">
26134  */
26135  
26136 /**
26137  * @class Roo.SplitButton
26138  * @extends Roo.Button
26139  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26140  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26141  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26142  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26143  * @cfg {String} arrowTooltip The title attribute of the arrow
26144  * @constructor
26145  * Create a new menu button
26146  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26147  * @param {Object} config The config object
26148  */
26149 Roo.SplitButton = function(renderTo, config){
26150     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26151     /**
26152      * @event arrowclick
26153      * Fires when this button's arrow is clicked
26154      * @param {SplitButton} this
26155      * @param {EventObject} e The click event
26156      */
26157     this.addEvents({"arrowclick":true});
26158 };
26159
26160 Roo.extend(Roo.SplitButton, Roo.Button, {
26161     render : function(renderTo){
26162         // this is one sweet looking template!
26163         var tpl = new Roo.Template(
26164             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26165             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26166             '<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>',
26167             "</tbody></table></td><td>",
26168             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26169             '<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>',
26170             "</tbody></table></td></tr></table>"
26171         );
26172         var btn = tpl.append(renderTo, [this.text, this.type], true);
26173         var btnEl = btn.child("button");
26174         if(this.cls){
26175             btn.addClass(this.cls);
26176         }
26177         if(this.icon){
26178             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26179         }
26180         if(this.iconCls){
26181             btnEl.addClass(this.iconCls);
26182             if(!this.cls){
26183                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26184             }
26185         }
26186         this.el = btn;
26187         if(this.handleMouseEvents){
26188             btn.on("mouseover", this.onMouseOver, this);
26189             btn.on("mouseout", this.onMouseOut, this);
26190             btn.on("mousedown", this.onMouseDown, this);
26191             btn.on("mouseup", this.onMouseUp, this);
26192         }
26193         btn.on(this.clickEvent, this.onClick, this);
26194         if(this.tooltip){
26195             if(typeof this.tooltip == 'object'){
26196                 Roo.QuickTips.tips(Roo.apply({
26197                       target: btnEl.id
26198                 }, this.tooltip));
26199             } else {
26200                 btnEl.dom[this.tooltipType] = this.tooltip;
26201             }
26202         }
26203         if(this.arrowTooltip){
26204             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26205         }
26206         if(this.hidden){
26207             this.hide();
26208         }
26209         if(this.disabled){
26210             this.disable();
26211         }
26212         if(this.pressed){
26213             this.el.addClass("x-btn-pressed");
26214         }
26215         if(Roo.isIE && !Roo.isIE7){
26216             this.autoWidth.defer(1, this);
26217         }else{
26218             this.autoWidth();
26219         }
26220         if(this.menu){
26221             this.menu.on("show", this.onMenuShow, this);
26222             this.menu.on("hide", this.onMenuHide, this);
26223         }
26224         this.fireEvent('render', this);
26225     },
26226
26227     // private
26228     autoWidth : function(){
26229         if(this.el){
26230             var tbl = this.el.child("table:first");
26231             var tbl2 = this.el.child("table:last");
26232             this.el.setWidth("auto");
26233             tbl.setWidth("auto");
26234             if(Roo.isIE7 && Roo.isStrict){
26235                 var ib = this.el.child('button:first');
26236                 if(ib && ib.getWidth() > 20){
26237                     ib.clip();
26238                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26239                 }
26240             }
26241             if(this.minWidth){
26242                 if(this.hidden){
26243                     this.el.beginMeasure();
26244                 }
26245                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26246                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26247                 }
26248                 if(this.hidden){
26249                     this.el.endMeasure();
26250                 }
26251             }
26252             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26253         } 
26254     },
26255     /**
26256      * Sets this button's click handler
26257      * @param {Function} handler The function to call when the button is clicked
26258      * @param {Object} scope (optional) Scope for the function passed above
26259      */
26260     setHandler : function(handler, scope){
26261         this.handler = handler;
26262         this.scope = scope;  
26263     },
26264     
26265     /**
26266      * Sets this button's arrow click handler
26267      * @param {Function} handler The function to call when the arrow is clicked
26268      * @param {Object} scope (optional) Scope for the function passed above
26269      */
26270     setArrowHandler : function(handler, scope){
26271         this.arrowHandler = handler;
26272         this.scope = scope;  
26273     },
26274     
26275     /**
26276      * Focus the button
26277      */
26278     focus : function(){
26279         if(this.el){
26280             this.el.child("button:first").focus();
26281         }
26282     },
26283
26284     // private
26285     onClick : function(e){
26286         e.preventDefault();
26287         if(!this.disabled){
26288             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26289                 if(this.menu && !this.menu.isVisible()){
26290                     this.menu.show(this.el, this.menuAlign);
26291                 }
26292                 this.fireEvent("arrowclick", this, e);
26293                 if(this.arrowHandler){
26294                     this.arrowHandler.call(this.scope || this, this, e);
26295                 }
26296             }else{
26297                 this.fireEvent("click", this, e);
26298                 if(this.handler){
26299                     this.handler.call(this.scope || this, this, e);
26300                 }
26301             }
26302         }
26303     },
26304     // private
26305     onMouseDown : function(e){
26306         if(!this.disabled){
26307             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26308         }
26309     },
26310     // private
26311     onMouseUp : function(e){
26312         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26313     }   
26314 });
26315
26316
26317 // backwards compat
26318 Roo.MenuButton = Roo.SplitButton;/*
26319  * Based on:
26320  * Ext JS Library 1.1.1
26321  * Copyright(c) 2006-2007, Ext JS, LLC.
26322  *
26323  * Originally Released Under LGPL - original licence link has changed is not relivant.
26324  *
26325  * Fork - LGPL
26326  * <script type="text/javascript">
26327  */
26328
26329 /**
26330  * @class Roo.Toolbar
26331  * Basic Toolbar class.
26332  * @constructor
26333  * Creates a new Toolbar
26334  * @param {Object} config The config object
26335  */ 
26336 Roo.Toolbar = function(container, buttons, config)
26337 {
26338     /// old consturctor format still supported..
26339     if(container instanceof Array){ // omit the container for later rendering
26340         buttons = container;
26341         config = buttons;
26342         container = null;
26343     }
26344     if (typeof(container) == 'object' && container.xtype) {
26345         config = container;
26346         container = config.container;
26347         buttons = config.buttons; // not really - use items!!
26348     }
26349     var xitems = [];
26350     if (config && config.items) {
26351         xitems = config.items;
26352         delete config.items;
26353     }
26354     Roo.apply(this, config);
26355     this.buttons = buttons;
26356     
26357     if(container){
26358         this.render(container);
26359     }
26360     Roo.each(xitems, function(b) {
26361         this.add(b);
26362     }, this);
26363     
26364 };
26365
26366 Roo.Toolbar.prototype = {
26367     /**
26368      * @cfg {Roo.data.Store} items
26369      * array of button configs or elements to add
26370      */
26371     
26372     /**
26373      * @cfg {String/HTMLElement/Element} container
26374      * The id or element that will contain the toolbar
26375      */
26376     // private
26377     render : function(ct){
26378         this.el = Roo.get(ct);
26379         if(this.cls){
26380             this.el.addClass(this.cls);
26381         }
26382         // using a table allows for vertical alignment
26383         // 100% width is needed by Safari...
26384         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26385         this.tr = this.el.child("tr", true);
26386         var autoId = 0;
26387         this.items = new Roo.util.MixedCollection(false, function(o){
26388             return o.id || ("item" + (++autoId));
26389         });
26390         if(this.buttons){
26391             this.add.apply(this, this.buttons);
26392             delete this.buttons;
26393         }
26394     },
26395
26396     /**
26397      * Adds element(s) to the toolbar -- this function takes a variable number of 
26398      * arguments of mixed type and adds them to the toolbar.
26399      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26400      * <ul>
26401      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26402      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26403      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26404      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26405      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26406      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26407      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26408      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26409      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26410      * </ul>
26411      * @param {Mixed} arg2
26412      * @param {Mixed} etc.
26413      */
26414     add : function(){
26415         var a = arguments, l = a.length;
26416         for(var i = 0; i < l; i++){
26417             this._add(a[i]);
26418         }
26419     },
26420     // private..
26421     _add : function(el) {
26422         
26423         if (el.xtype) {
26424             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26425         }
26426         
26427         if (el.applyTo){ // some kind of form field
26428             return this.addField(el);
26429         } 
26430         if (el.render){ // some kind of Toolbar.Item
26431             return this.addItem(el);
26432         }
26433         if (typeof el == "string"){ // string
26434             if(el == "separator" || el == "-"){
26435                 return this.addSeparator();
26436             }
26437             if (el == " "){
26438                 return this.addSpacer();
26439             }
26440             if(el == "->"){
26441                 return this.addFill();
26442             }
26443             return this.addText(el);
26444             
26445         }
26446         if(el.tagName){ // element
26447             return this.addElement(el);
26448         }
26449         if(typeof el == "object"){ // must be button config?
26450             return this.addButton(el);
26451         }
26452         // and now what?!?!
26453         return false;
26454         
26455     },
26456     
26457     /**
26458      * Add an Xtype element
26459      * @param {Object} xtype Xtype Object
26460      * @return {Object} created Object
26461      */
26462     addxtype : function(e){
26463         return this.add(e);  
26464     },
26465     
26466     /**
26467      * Returns the Element for this toolbar.
26468      * @return {Roo.Element}
26469      */
26470     getEl : function(){
26471         return this.el;  
26472     },
26473     
26474     /**
26475      * Adds a separator
26476      * @return {Roo.Toolbar.Item} The separator item
26477      */
26478     addSeparator : function(){
26479         return this.addItem(new Roo.Toolbar.Separator());
26480     },
26481
26482     /**
26483      * Adds a spacer element
26484      * @return {Roo.Toolbar.Spacer} The spacer item
26485      */
26486     addSpacer : function(){
26487         return this.addItem(new Roo.Toolbar.Spacer());
26488     },
26489
26490     /**
26491      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26492      * @return {Roo.Toolbar.Fill} The fill item
26493      */
26494     addFill : function(){
26495         return this.addItem(new Roo.Toolbar.Fill());
26496     },
26497
26498     /**
26499      * Adds any standard HTML element to the toolbar
26500      * @param {String/HTMLElement/Element} el The element or id of the element to add
26501      * @return {Roo.Toolbar.Item} The element's item
26502      */
26503     addElement : function(el){
26504         return this.addItem(new Roo.Toolbar.Item(el));
26505     },
26506     /**
26507      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26508      * @type Roo.util.MixedCollection  
26509      */
26510     items : false,
26511      
26512     /**
26513      * Adds any Toolbar.Item or subclass
26514      * @param {Roo.Toolbar.Item} item
26515      * @return {Roo.Toolbar.Item} The item
26516      */
26517     addItem : function(item){
26518         var td = this.nextBlock();
26519         item.render(td);
26520         this.items.add(item);
26521         return item;
26522     },
26523     
26524     /**
26525      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26526      * @param {Object/Array} config A button config or array of configs
26527      * @return {Roo.Toolbar.Button/Array}
26528      */
26529     addButton : function(config){
26530         if(config instanceof Array){
26531             var buttons = [];
26532             for(var i = 0, len = config.length; i < len; i++) {
26533                 buttons.push(this.addButton(config[i]));
26534             }
26535             return buttons;
26536         }
26537         var b = config;
26538         if(!(config instanceof Roo.Toolbar.Button)){
26539             b = config.split ?
26540                 new Roo.Toolbar.SplitButton(config) :
26541                 new Roo.Toolbar.Button(config);
26542         }
26543         var td = this.nextBlock();
26544         b.render(td);
26545         this.items.add(b);
26546         return b;
26547     },
26548     
26549     /**
26550      * Adds text to the toolbar
26551      * @param {String} text The text to add
26552      * @return {Roo.Toolbar.Item} The element's item
26553      */
26554     addText : function(text){
26555         return this.addItem(new Roo.Toolbar.TextItem(text));
26556     },
26557     
26558     /**
26559      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26560      * @param {Number} index The index where the item is to be inserted
26561      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26562      * @return {Roo.Toolbar.Button/Item}
26563      */
26564     insertButton : function(index, item){
26565         if(item instanceof Array){
26566             var buttons = [];
26567             for(var i = 0, len = item.length; i < len; i++) {
26568                buttons.push(this.insertButton(index + i, item[i]));
26569             }
26570             return buttons;
26571         }
26572         if (!(item instanceof Roo.Toolbar.Button)){
26573            item = new Roo.Toolbar.Button(item);
26574         }
26575         var td = document.createElement("td");
26576         this.tr.insertBefore(td, this.tr.childNodes[index]);
26577         item.render(td);
26578         this.items.insert(index, item);
26579         return item;
26580     },
26581     
26582     /**
26583      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26584      * @param {Object} config
26585      * @return {Roo.Toolbar.Item} The element's item
26586      */
26587     addDom : function(config, returnEl){
26588         var td = this.nextBlock();
26589         Roo.DomHelper.overwrite(td, config);
26590         var ti = new Roo.Toolbar.Item(td.firstChild);
26591         ti.render(td);
26592         this.items.add(ti);
26593         return ti;
26594     },
26595
26596     /**
26597      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26598      * @type Roo.util.MixedCollection  
26599      */
26600     fields : false,
26601     
26602     /**
26603      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26604      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26605      * @param {Roo.form.Field} field
26606      * @return {Roo.ToolbarItem}
26607      */
26608      
26609       
26610     addField : function(field) {
26611         if (!this.fields) {
26612             var autoId = 0;
26613             this.fields = new Roo.util.MixedCollection(false, function(o){
26614                 return o.id || ("item" + (++autoId));
26615             });
26616
26617         }
26618         
26619         var td = this.nextBlock();
26620         field.render(td);
26621         var ti = new Roo.Toolbar.Item(td.firstChild);
26622         ti.render(td);
26623         this.items.add(ti);
26624         this.fields.add(field);
26625         return ti;
26626     },
26627     /**
26628      * Hide the toolbar
26629      * @method hide
26630      */
26631      
26632       
26633     hide : function()
26634     {
26635         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26636         this.el.child('div').hide();
26637     },
26638     /**
26639      * Show the toolbar
26640      * @method show
26641      */
26642     show : function()
26643     {
26644         this.el.child('div').show();
26645     },
26646       
26647     // private
26648     nextBlock : function(){
26649         var td = document.createElement("td");
26650         this.tr.appendChild(td);
26651         return td;
26652     },
26653
26654     // private
26655     destroy : function(){
26656         if(this.items){ // rendered?
26657             Roo.destroy.apply(Roo, this.items.items);
26658         }
26659         if(this.fields){ // rendered?
26660             Roo.destroy.apply(Roo, this.fields.items);
26661         }
26662         Roo.Element.uncache(this.el, this.tr);
26663     }
26664 };
26665
26666 /**
26667  * @class Roo.Toolbar.Item
26668  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26669  * @constructor
26670  * Creates a new Item
26671  * @param {HTMLElement} el 
26672  */
26673 Roo.Toolbar.Item = function(el){
26674     this.el = Roo.getDom(el);
26675     this.id = Roo.id(this.el);
26676     this.hidden = false;
26677 };
26678
26679 Roo.Toolbar.Item.prototype = {
26680     
26681     /**
26682      * Get this item's HTML Element
26683      * @return {HTMLElement}
26684      */
26685     getEl : function(){
26686        return this.el;  
26687     },
26688
26689     // private
26690     render : function(td){
26691         this.td = td;
26692         td.appendChild(this.el);
26693     },
26694     
26695     /**
26696      * Removes and destroys this item.
26697      */
26698     destroy : function(){
26699         this.td.parentNode.removeChild(this.td);
26700     },
26701     
26702     /**
26703      * Shows this item.
26704      */
26705     show: function(){
26706         this.hidden = false;
26707         this.td.style.display = "";
26708     },
26709     
26710     /**
26711      * Hides this item.
26712      */
26713     hide: function(){
26714         this.hidden = true;
26715         this.td.style.display = "none";
26716     },
26717     
26718     /**
26719      * Convenience function for boolean show/hide.
26720      * @param {Boolean} visible true to show/false to hide
26721      */
26722     setVisible: function(visible){
26723         if(visible) {
26724             this.show();
26725         }else{
26726             this.hide();
26727         }
26728     },
26729     
26730     /**
26731      * Try to focus this item.
26732      */
26733     focus : function(){
26734         Roo.fly(this.el).focus();
26735     },
26736     
26737     /**
26738      * Disables this item.
26739      */
26740     disable : function(){
26741         Roo.fly(this.td).addClass("x-item-disabled");
26742         this.disabled = true;
26743         this.el.disabled = true;
26744     },
26745     
26746     /**
26747      * Enables this item.
26748      */
26749     enable : function(){
26750         Roo.fly(this.td).removeClass("x-item-disabled");
26751         this.disabled = false;
26752         this.el.disabled = false;
26753     }
26754 };
26755
26756
26757 /**
26758  * @class Roo.Toolbar.Separator
26759  * @extends Roo.Toolbar.Item
26760  * A simple toolbar separator class
26761  * @constructor
26762  * Creates a new Separator
26763  */
26764 Roo.Toolbar.Separator = function(){
26765     var s = document.createElement("span");
26766     s.className = "ytb-sep";
26767     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26768 };
26769 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26770     enable:Roo.emptyFn,
26771     disable:Roo.emptyFn,
26772     focus:Roo.emptyFn
26773 });
26774
26775 /**
26776  * @class Roo.Toolbar.Spacer
26777  * @extends Roo.Toolbar.Item
26778  * A simple element that adds extra horizontal space to a toolbar.
26779  * @constructor
26780  * Creates a new Spacer
26781  */
26782 Roo.Toolbar.Spacer = function(){
26783     var s = document.createElement("div");
26784     s.className = "ytb-spacer";
26785     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26786 };
26787 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26788     enable:Roo.emptyFn,
26789     disable:Roo.emptyFn,
26790     focus:Roo.emptyFn
26791 });
26792
26793 /**
26794  * @class Roo.Toolbar.Fill
26795  * @extends Roo.Toolbar.Spacer
26796  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26797  * @constructor
26798  * Creates a new Spacer
26799  */
26800 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26801     // private
26802     render : function(td){
26803         td.style.width = '100%';
26804         Roo.Toolbar.Fill.superclass.render.call(this, td);
26805     }
26806 });
26807
26808 /**
26809  * @class Roo.Toolbar.TextItem
26810  * @extends Roo.Toolbar.Item
26811  * A simple class that renders text directly into a toolbar.
26812  * @constructor
26813  * Creates a new TextItem
26814  * @param {String} text
26815  */
26816 Roo.Toolbar.TextItem = function(text){
26817     if (typeof(text) == 'object') {
26818         text = text.text;
26819     }
26820     var s = document.createElement("span");
26821     s.className = "ytb-text";
26822     s.innerHTML = text;
26823     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26824 };
26825 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26826     enable:Roo.emptyFn,
26827     disable:Roo.emptyFn,
26828     focus:Roo.emptyFn
26829 });
26830
26831 /**
26832  * @class Roo.Toolbar.Button
26833  * @extends Roo.Button
26834  * A button that renders into a toolbar.
26835  * @constructor
26836  * Creates a new Button
26837  * @param {Object} config A standard {@link Roo.Button} config object
26838  */
26839 Roo.Toolbar.Button = function(config){
26840     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26841 };
26842 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26843     render : function(td){
26844         this.td = td;
26845         Roo.Toolbar.Button.superclass.render.call(this, td);
26846     },
26847     
26848     /**
26849      * Removes and destroys this button
26850      */
26851     destroy : function(){
26852         Roo.Toolbar.Button.superclass.destroy.call(this);
26853         this.td.parentNode.removeChild(this.td);
26854     },
26855     
26856     /**
26857      * Shows this button
26858      */
26859     show: function(){
26860         this.hidden = false;
26861         this.td.style.display = "";
26862     },
26863     
26864     /**
26865      * Hides this button
26866      */
26867     hide: function(){
26868         this.hidden = true;
26869         this.td.style.display = "none";
26870     },
26871
26872     /**
26873      * Disables this item
26874      */
26875     disable : function(){
26876         Roo.fly(this.td).addClass("x-item-disabled");
26877         this.disabled = true;
26878     },
26879
26880     /**
26881      * Enables this item
26882      */
26883     enable : function(){
26884         Roo.fly(this.td).removeClass("x-item-disabled");
26885         this.disabled = false;
26886     }
26887 });
26888 // backwards compat
26889 Roo.ToolbarButton = Roo.Toolbar.Button;
26890
26891 /**
26892  * @class Roo.Toolbar.SplitButton
26893  * @extends Roo.SplitButton
26894  * A menu button that renders into a toolbar.
26895  * @constructor
26896  * Creates a new SplitButton
26897  * @param {Object} config A standard {@link Roo.SplitButton} config object
26898  */
26899 Roo.Toolbar.SplitButton = function(config){
26900     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26901 };
26902 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26903     render : function(td){
26904         this.td = td;
26905         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26906     },
26907     
26908     /**
26909      * Removes and destroys this button
26910      */
26911     destroy : function(){
26912         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26913         this.td.parentNode.removeChild(this.td);
26914     },
26915     
26916     /**
26917      * Shows this button
26918      */
26919     show: function(){
26920         this.hidden = false;
26921         this.td.style.display = "";
26922     },
26923     
26924     /**
26925      * Hides this button
26926      */
26927     hide: function(){
26928         this.hidden = true;
26929         this.td.style.display = "none";
26930     }
26931 });
26932
26933 // backwards compat
26934 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26935  * Based on:
26936  * Ext JS Library 1.1.1
26937  * Copyright(c) 2006-2007, Ext JS, LLC.
26938  *
26939  * Originally Released Under LGPL - original licence link has changed is not relivant.
26940  *
26941  * Fork - LGPL
26942  * <script type="text/javascript">
26943  */
26944  
26945 /**
26946  * @class Roo.PagingToolbar
26947  * @extends Roo.Toolbar
26948  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26949  * @constructor
26950  * Create a new PagingToolbar
26951  * @param {Object} config The config object
26952  */
26953 Roo.PagingToolbar = function(el, ds, config)
26954 {
26955     // old args format still supported... - xtype is prefered..
26956     if (typeof(el) == 'object' && el.xtype) {
26957         // created from xtype...
26958         config = el;
26959         ds = el.dataSource;
26960         el = config.container;
26961     }
26962     
26963     
26964     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26965     this.ds = ds;
26966     this.cursor = 0;
26967     this.renderButtons(this.el);
26968     this.bind(ds);
26969 };
26970
26971 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26972     /**
26973      * @cfg {Roo.data.Store} dataSource
26974      * The underlying data store providing the paged data
26975      */
26976     /**
26977      * @cfg {String/HTMLElement/Element} container
26978      * container The id or element that will contain the toolbar
26979      */
26980     /**
26981      * @cfg {Boolean} displayInfo
26982      * True to display the displayMsg (defaults to false)
26983      */
26984     /**
26985      * @cfg {Number} pageSize
26986      * The number of records to display per page (defaults to 20)
26987      */
26988     pageSize: 20,
26989     /**
26990      * @cfg {String} displayMsg
26991      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26992      */
26993     displayMsg : 'Displaying {0} - {1} of {2}',
26994     /**
26995      * @cfg {String} emptyMsg
26996      * The message to display when no records are found (defaults to "No data to display")
26997      */
26998     emptyMsg : 'No data to display',
26999     /**
27000      * Customizable piece of the default paging text (defaults to "Page")
27001      * @type String
27002      */
27003     beforePageText : "Page",
27004     /**
27005      * Customizable piece of the default paging text (defaults to "of %0")
27006      * @type String
27007      */
27008     afterPageText : "of {0}",
27009     /**
27010      * Customizable piece of the default paging text (defaults to "First Page")
27011      * @type String
27012      */
27013     firstText : "First Page",
27014     /**
27015      * Customizable piece of the default paging text (defaults to "Previous Page")
27016      * @type String
27017      */
27018     prevText : "Previous Page",
27019     /**
27020      * Customizable piece of the default paging text (defaults to "Next Page")
27021      * @type String
27022      */
27023     nextText : "Next Page",
27024     /**
27025      * Customizable piece of the default paging text (defaults to "Last Page")
27026      * @type String
27027      */
27028     lastText : "Last Page",
27029     /**
27030      * Customizable piece of the default paging text (defaults to "Refresh")
27031      * @type String
27032      */
27033     refreshText : "Refresh",
27034
27035     // private
27036     renderButtons : function(el){
27037         Roo.PagingToolbar.superclass.render.call(this, el);
27038         this.first = this.addButton({
27039             tooltip: this.firstText,
27040             cls: "x-btn-icon x-grid-page-first",
27041             disabled: true,
27042             handler: this.onClick.createDelegate(this, ["first"])
27043         });
27044         this.prev = this.addButton({
27045             tooltip: this.prevText,
27046             cls: "x-btn-icon x-grid-page-prev",
27047             disabled: true,
27048             handler: this.onClick.createDelegate(this, ["prev"])
27049         });
27050         this.addSeparator();
27051         this.add(this.beforePageText);
27052         this.field = Roo.get(this.addDom({
27053            tag: "input",
27054            type: "text",
27055            size: "3",
27056            value: "1",
27057            cls: "x-grid-page-number"
27058         }).el);
27059         this.field.on("keydown", this.onPagingKeydown, this);
27060         this.field.on("focus", function(){this.dom.select();});
27061         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27062         this.field.setHeight(18);
27063         this.addSeparator();
27064         this.next = this.addButton({
27065             tooltip: this.nextText,
27066             cls: "x-btn-icon x-grid-page-next",
27067             disabled: true,
27068             handler: this.onClick.createDelegate(this, ["next"])
27069         });
27070         this.last = this.addButton({
27071             tooltip: this.lastText,
27072             cls: "x-btn-icon x-grid-page-last",
27073             disabled: true,
27074             handler: this.onClick.createDelegate(this, ["last"])
27075         });
27076         this.addSeparator();
27077         this.loading = this.addButton({
27078             tooltip: this.refreshText,
27079             cls: "x-btn-icon x-grid-loading",
27080             handler: this.onClick.createDelegate(this, ["refresh"])
27081         });
27082
27083         if(this.displayInfo){
27084             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27085         }
27086     },
27087
27088     // private
27089     updateInfo : function(){
27090         if(this.displayEl){
27091             var count = this.ds.getCount();
27092             var msg = count == 0 ?
27093                 this.emptyMsg :
27094                 String.format(
27095                     this.displayMsg,
27096                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27097                 );
27098             this.displayEl.update(msg);
27099         }
27100     },
27101
27102     // private
27103     onLoad : function(ds, r, o){
27104        this.cursor = o.params ? o.params.start : 0;
27105        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27106
27107        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27108        this.field.dom.value = ap;
27109        this.first.setDisabled(ap == 1);
27110        this.prev.setDisabled(ap == 1);
27111        this.next.setDisabled(ap == ps);
27112        this.last.setDisabled(ap == ps);
27113        this.loading.enable();
27114        this.updateInfo();
27115     },
27116
27117     // private
27118     getPageData : function(){
27119         var total = this.ds.getTotalCount();
27120         return {
27121             total : total,
27122             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27123             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27124         };
27125     },
27126
27127     // private
27128     onLoadError : function(){
27129         this.loading.enable();
27130     },
27131
27132     // private
27133     onPagingKeydown : function(e){
27134         var k = e.getKey();
27135         var d = this.getPageData();
27136         if(k == e.RETURN){
27137             var v = this.field.dom.value, pageNum;
27138             if(!v || isNaN(pageNum = parseInt(v, 10))){
27139                 this.field.dom.value = d.activePage;
27140                 return;
27141             }
27142             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27143             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27144             e.stopEvent();
27145         }
27146         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))
27147         {
27148           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27149           this.field.dom.value = pageNum;
27150           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27151           e.stopEvent();
27152         }
27153         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27154         {
27155           var v = this.field.dom.value, pageNum; 
27156           var increment = (e.shiftKey) ? 10 : 1;
27157           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27158             increment *= -1;
27159           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27160             this.field.dom.value = d.activePage;
27161             return;
27162           }
27163           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27164           {
27165             this.field.dom.value = parseInt(v, 10) + increment;
27166             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27167             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27168           }
27169           e.stopEvent();
27170         }
27171     },
27172
27173     // private
27174     beforeLoad : function(){
27175         if(this.loading){
27176             this.loading.disable();
27177         }
27178     },
27179
27180     // private
27181     onClick : function(which){
27182         var ds = this.ds;
27183         switch(which){
27184             case "first":
27185                 ds.load({params:{start: 0, limit: this.pageSize}});
27186             break;
27187             case "prev":
27188                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27189             break;
27190             case "next":
27191                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27192             break;
27193             case "last":
27194                 var total = ds.getTotalCount();
27195                 var extra = total % this.pageSize;
27196                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27197                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27198             break;
27199             case "refresh":
27200                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27201             break;
27202         }
27203     },
27204
27205     /**
27206      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27207      * @param {Roo.data.Store} store The data store to unbind
27208      */
27209     unbind : function(ds){
27210         ds.un("beforeload", this.beforeLoad, this);
27211         ds.un("load", this.onLoad, this);
27212         ds.un("loadexception", this.onLoadError, this);
27213         ds.un("remove", this.updateInfo, this);
27214         ds.un("add", this.updateInfo, this);
27215         this.ds = undefined;
27216     },
27217
27218     /**
27219      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27220      * @param {Roo.data.Store} store The data store to bind
27221      */
27222     bind : function(ds){
27223         ds.on("beforeload", this.beforeLoad, this);
27224         ds.on("load", this.onLoad, this);
27225         ds.on("loadexception", this.onLoadError, this);
27226         ds.on("remove", this.updateInfo, this);
27227         ds.on("add", this.updateInfo, this);
27228         this.ds = ds;
27229     }
27230 });/*
27231  * Based on:
27232  * Ext JS Library 1.1.1
27233  * Copyright(c) 2006-2007, Ext JS, LLC.
27234  *
27235  * Originally Released Under LGPL - original licence link has changed is not relivant.
27236  *
27237  * Fork - LGPL
27238  * <script type="text/javascript">
27239  */
27240
27241 /**
27242  * @class Roo.Resizable
27243  * @extends Roo.util.Observable
27244  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27245  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27246  * 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
27247  * the element will be wrapped for you automatically.</p>
27248  * <p>Here is the list of valid resize handles:</p>
27249  * <pre>
27250 Value   Description
27251 ------  -------------------
27252  'n'     north
27253  's'     south
27254  'e'     east
27255  'w'     west
27256  'nw'    northwest
27257  'sw'    southwest
27258  'se'    southeast
27259  'ne'    northeast
27260  'all'   all
27261 </pre>
27262  * <p>Here's an example showing the creation of a typical Resizable:</p>
27263  * <pre><code>
27264 var resizer = new Roo.Resizable("element-id", {
27265     handles: 'all',
27266     minWidth: 200,
27267     minHeight: 100,
27268     maxWidth: 500,
27269     maxHeight: 400,
27270     pinned: true
27271 });
27272 resizer.on("resize", myHandler);
27273 </code></pre>
27274  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27275  * resizer.east.setDisplayed(false);</p>
27276  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27277  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27278  * resize operation's new size (defaults to [0, 0])
27279  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27280  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27281  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27282  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27283  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27284  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27285  * @cfg {Number} width The width of the element in pixels (defaults to null)
27286  * @cfg {Number} height The height of the element in pixels (defaults to null)
27287  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27288  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27289  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27290  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27291  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27292  * in favor of the handles config option (defaults to false)
27293  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27294  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27295  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27296  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27297  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27298  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27299  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27300  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27301  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27302  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27303  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27304  * @constructor
27305  * Create a new resizable component
27306  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27307  * @param {Object} config configuration options
27308   */
27309 Roo.Resizable = function(el, config){
27310     this.el = Roo.get(el);
27311
27312     if(config && config.wrap){
27313         config.resizeChild = this.el;
27314         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27315         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27316         this.el.setStyle("overflow", "hidden");
27317         this.el.setPositioning(config.resizeChild.getPositioning());
27318         config.resizeChild.clearPositioning();
27319         if(!config.width || !config.height){
27320             var csize = config.resizeChild.getSize();
27321             this.el.setSize(csize.width, csize.height);
27322         }
27323         if(config.pinned && !config.adjustments){
27324             config.adjustments = "auto";
27325         }
27326     }
27327
27328     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27329     this.proxy.unselectable();
27330     this.proxy.enableDisplayMode('block');
27331
27332     Roo.apply(this, config);
27333
27334     if(this.pinned){
27335         this.disableTrackOver = true;
27336         this.el.addClass("x-resizable-pinned");
27337     }
27338     // if the element isn't positioned, make it relative
27339     var position = this.el.getStyle("position");
27340     if(position != "absolute" && position != "fixed"){
27341         this.el.setStyle("position", "relative");
27342     }
27343     if(!this.handles){ // no handles passed, must be legacy style
27344         this.handles = 's,e,se';
27345         if(this.multiDirectional){
27346             this.handles += ',n,w';
27347         }
27348     }
27349     if(this.handles == "all"){
27350         this.handles = "n s e w ne nw se sw";
27351     }
27352     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27353     var ps = Roo.Resizable.positions;
27354     for(var i = 0, len = hs.length; i < len; i++){
27355         if(hs[i] && ps[hs[i]]){
27356             var pos = ps[hs[i]];
27357             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27358         }
27359     }
27360     // legacy
27361     this.corner = this.southeast;
27362
27363     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27364         this.updateBox = true;
27365     }
27366
27367     this.activeHandle = null;
27368
27369     if(this.resizeChild){
27370         if(typeof this.resizeChild == "boolean"){
27371             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27372         }else{
27373             this.resizeChild = Roo.get(this.resizeChild, true);
27374         }
27375     }
27376
27377     if(this.adjustments == "auto"){
27378         var rc = this.resizeChild;
27379         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27380         if(rc && (hw || hn)){
27381             rc.position("relative");
27382             rc.setLeft(hw ? hw.el.getWidth() : 0);
27383             rc.setTop(hn ? hn.el.getHeight() : 0);
27384         }
27385         this.adjustments = [
27386             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27387             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27388         ];
27389     }
27390
27391     if(this.draggable){
27392         this.dd = this.dynamic ?
27393             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27394         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27395     }
27396
27397     // public events
27398     this.addEvents({
27399         /**
27400          * @event beforeresize
27401          * Fired before resize is allowed. Set enabled to false to cancel resize.
27402          * @param {Roo.Resizable} this
27403          * @param {Roo.EventObject} e The mousedown event
27404          */
27405         "beforeresize" : true,
27406         /**
27407          * @event resize
27408          * Fired after a resize.
27409          * @param {Roo.Resizable} this
27410          * @param {Number} width The new width
27411          * @param {Number} height The new height
27412          * @param {Roo.EventObject} e The mouseup event
27413          */
27414         "resize" : true
27415     });
27416
27417     if(this.width !== null && this.height !== null){
27418         this.resizeTo(this.width, this.height);
27419     }else{
27420         this.updateChildSize();
27421     }
27422     if(Roo.isIE){
27423         this.el.dom.style.zoom = 1;
27424     }
27425     Roo.Resizable.superclass.constructor.call(this);
27426 };
27427
27428 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27429         resizeChild : false,
27430         adjustments : [0, 0],
27431         minWidth : 5,
27432         minHeight : 5,
27433         maxWidth : 10000,
27434         maxHeight : 10000,
27435         enabled : true,
27436         animate : false,
27437         duration : .35,
27438         dynamic : false,
27439         handles : false,
27440         multiDirectional : false,
27441         disableTrackOver : false,
27442         easing : 'easeOutStrong',
27443         widthIncrement : 0,
27444         heightIncrement : 0,
27445         pinned : false,
27446         width : null,
27447         height : null,
27448         preserveRatio : false,
27449         transparent: false,
27450         minX: 0,
27451         minY: 0,
27452         draggable: false,
27453
27454         /**
27455          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27456          */
27457         constrainTo: undefined,
27458         /**
27459          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27460          */
27461         resizeRegion: undefined,
27462
27463
27464     /**
27465      * Perform a manual resize
27466      * @param {Number} width
27467      * @param {Number} height
27468      */
27469     resizeTo : function(width, height){
27470         this.el.setSize(width, height);
27471         this.updateChildSize();
27472         this.fireEvent("resize", this, width, height, null);
27473     },
27474
27475     // private
27476     startSizing : function(e, handle){
27477         this.fireEvent("beforeresize", this, e);
27478         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27479
27480             if(!this.overlay){
27481                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27482                 this.overlay.unselectable();
27483                 this.overlay.enableDisplayMode("block");
27484                 this.overlay.on("mousemove", this.onMouseMove, this);
27485                 this.overlay.on("mouseup", this.onMouseUp, this);
27486             }
27487             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27488
27489             this.resizing = true;
27490             this.startBox = this.el.getBox();
27491             this.startPoint = e.getXY();
27492             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27493                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27494
27495             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27496             this.overlay.show();
27497
27498             if(this.constrainTo) {
27499                 var ct = Roo.get(this.constrainTo);
27500                 this.resizeRegion = ct.getRegion().adjust(
27501                     ct.getFrameWidth('t'),
27502                     ct.getFrameWidth('l'),
27503                     -ct.getFrameWidth('b'),
27504                     -ct.getFrameWidth('r')
27505                 );
27506             }
27507
27508             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27509             this.proxy.show();
27510             this.proxy.setBox(this.startBox);
27511             if(!this.dynamic){
27512                 this.proxy.setStyle('visibility', 'visible');
27513             }
27514         }
27515     },
27516
27517     // private
27518     onMouseDown : function(handle, e){
27519         if(this.enabled){
27520             e.stopEvent();
27521             this.activeHandle = handle;
27522             this.startSizing(e, handle);
27523         }
27524     },
27525
27526     // private
27527     onMouseUp : function(e){
27528         var size = this.resizeElement();
27529         this.resizing = false;
27530         this.handleOut();
27531         this.overlay.hide();
27532         this.proxy.hide();
27533         this.fireEvent("resize", this, size.width, size.height, e);
27534     },
27535
27536     // private
27537     updateChildSize : function(){
27538         if(this.resizeChild){
27539             var el = this.el;
27540             var child = this.resizeChild;
27541             var adj = this.adjustments;
27542             if(el.dom.offsetWidth){
27543                 var b = el.getSize(true);
27544                 child.setSize(b.width+adj[0], b.height+adj[1]);
27545             }
27546             // Second call here for IE
27547             // The first call enables instant resizing and
27548             // the second call corrects scroll bars if they
27549             // exist
27550             if(Roo.isIE){
27551                 setTimeout(function(){
27552                     if(el.dom.offsetWidth){
27553                         var b = el.getSize(true);
27554                         child.setSize(b.width+adj[0], b.height+adj[1]);
27555                     }
27556                 }, 10);
27557             }
27558         }
27559     },
27560
27561     // private
27562     snap : function(value, inc, min){
27563         if(!inc || !value) return value;
27564         var newValue = value;
27565         var m = value % inc;
27566         if(m > 0){
27567             if(m > (inc/2)){
27568                 newValue = value + (inc-m);
27569             }else{
27570                 newValue = value - m;
27571             }
27572         }
27573         return Math.max(min, newValue);
27574     },
27575
27576     // private
27577     resizeElement : function(){
27578         var box = this.proxy.getBox();
27579         if(this.updateBox){
27580             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27581         }else{
27582             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27583         }
27584         this.updateChildSize();
27585         if(!this.dynamic){
27586             this.proxy.hide();
27587         }
27588         return box;
27589     },
27590
27591     // private
27592     constrain : function(v, diff, m, mx){
27593         if(v - diff < m){
27594             diff = v - m;
27595         }else if(v - diff > mx){
27596             diff = mx - v;
27597         }
27598         return diff;
27599     },
27600
27601     // private
27602     onMouseMove : function(e){
27603         if(this.enabled){
27604             try{// try catch so if something goes wrong the user doesn't get hung
27605
27606             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27607                 return;
27608             }
27609
27610             //var curXY = this.startPoint;
27611             var curSize = this.curSize || this.startBox;
27612             var x = this.startBox.x, y = this.startBox.y;
27613             var ox = x, oy = y;
27614             var w = curSize.width, h = curSize.height;
27615             var ow = w, oh = h;
27616             var mw = this.minWidth, mh = this.minHeight;
27617             var mxw = this.maxWidth, mxh = this.maxHeight;
27618             var wi = this.widthIncrement;
27619             var hi = this.heightIncrement;
27620
27621             var eventXY = e.getXY();
27622             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27623             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27624
27625             var pos = this.activeHandle.position;
27626
27627             switch(pos){
27628                 case "east":
27629                     w += diffX;
27630                     w = Math.min(Math.max(mw, w), mxw);
27631                     break;
27632                 case "south":
27633                     h += diffY;
27634                     h = Math.min(Math.max(mh, h), mxh);
27635                     break;
27636                 case "southeast":
27637                     w += diffX;
27638                     h += diffY;
27639                     w = Math.min(Math.max(mw, w), mxw);
27640                     h = Math.min(Math.max(mh, h), mxh);
27641                     break;
27642                 case "north":
27643                     diffY = this.constrain(h, diffY, mh, mxh);
27644                     y += diffY;
27645                     h -= diffY;
27646                     break;
27647                 case "west":
27648                     diffX = this.constrain(w, diffX, mw, mxw);
27649                     x += diffX;
27650                     w -= diffX;
27651                     break;
27652                 case "northeast":
27653                     w += diffX;
27654                     w = Math.min(Math.max(mw, w), mxw);
27655                     diffY = this.constrain(h, diffY, mh, mxh);
27656                     y += diffY;
27657                     h -= diffY;
27658                     break;
27659                 case "northwest":
27660                     diffX = this.constrain(w, diffX, mw, mxw);
27661                     diffY = this.constrain(h, diffY, mh, mxh);
27662                     y += diffY;
27663                     h -= diffY;
27664                     x += diffX;
27665                     w -= diffX;
27666                     break;
27667                case "southwest":
27668                     diffX = this.constrain(w, diffX, mw, mxw);
27669                     h += diffY;
27670                     h = Math.min(Math.max(mh, h), mxh);
27671                     x += diffX;
27672                     w -= diffX;
27673                     break;
27674             }
27675
27676             var sw = this.snap(w, wi, mw);
27677             var sh = this.snap(h, hi, mh);
27678             if(sw != w || sh != h){
27679                 switch(pos){
27680                     case "northeast":
27681                         y -= sh - h;
27682                     break;
27683                     case "north":
27684                         y -= sh - h;
27685                         break;
27686                     case "southwest":
27687                         x -= sw - w;
27688                     break;
27689                     case "west":
27690                         x -= sw - w;
27691                         break;
27692                     case "northwest":
27693                         x -= sw - w;
27694                         y -= sh - h;
27695                     break;
27696                 }
27697                 w = sw;
27698                 h = sh;
27699             }
27700
27701             if(this.preserveRatio){
27702                 switch(pos){
27703                     case "southeast":
27704                     case "east":
27705                         h = oh * (w/ow);
27706                         h = Math.min(Math.max(mh, h), mxh);
27707                         w = ow * (h/oh);
27708                        break;
27709                     case "south":
27710                         w = ow * (h/oh);
27711                         w = Math.min(Math.max(mw, w), mxw);
27712                         h = oh * (w/ow);
27713                         break;
27714                     case "northeast":
27715                         w = ow * (h/oh);
27716                         w = Math.min(Math.max(mw, w), mxw);
27717                         h = oh * (w/ow);
27718                     break;
27719                     case "north":
27720                         var tw = w;
27721                         w = ow * (h/oh);
27722                         w = Math.min(Math.max(mw, w), mxw);
27723                         h = oh * (w/ow);
27724                         x += (tw - w) / 2;
27725                         break;
27726                     case "southwest":
27727                         h = oh * (w/ow);
27728                         h = Math.min(Math.max(mh, h), mxh);
27729                         var tw = w;
27730                         w = ow * (h/oh);
27731                         x += tw - w;
27732                         break;
27733                     case "west":
27734                         var th = h;
27735                         h = oh * (w/ow);
27736                         h = Math.min(Math.max(mh, h), mxh);
27737                         y += (th - h) / 2;
27738                         var tw = w;
27739                         w = ow * (h/oh);
27740                         x += tw - w;
27741                        break;
27742                     case "northwest":
27743                         var tw = w;
27744                         var th = h;
27745                         h = oh * (w/ow);
27746                         h = Math.min(Math.max(mh, h), mxh);
27747                         w = ow * (h/oh);
27748                         y += th - h;
27749                          x += tw - w;
27750                        break;
27751
27752                 }
27753             }
27754             this.proxy.setBounds(x, y, w, h);
27755             if(this.dynamic){
27756                 this.resizeElement();
27757             }
27758             }catch(e){}
27759         }
27760     },
27761
27762     // private
27763     handleOver : function(){
27764         if(this.enabled){
27765             this.el.addClass("x-resizable-over");
27766         }
27767     },
27768
27769     // private
27770     handleOut : function(){
27771         if(!this.resizing){
27772             this.el.removeClass("x-resizable-over");
27773         }
27774     },
27775
27776     /**
27777      * Returns the element this component is bound to.
27778      * @return {Roo.Element}
27779      */
27780     getEl : function(){
27781         return this.el;
27782     },
27783
27784     /**
27785      * Returns the resizeChild element (or null).
27786      * @return {Roo.Element}
27787      */
27788     getResizeChild : function(){
27789         return this.resizeChild;
27790     },
27791
27792     /**
27793      * Destroys this resizable. If the element was wrapped and
27794      * removeEl is not true then the element remains.
27795      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27796      */
27797     destroy : function(removeEl){
27798         this.proxy.remove();
27799         if(this.overlay){
27800             this.overlay.removeAllListeners();
27801             this.overlay.remove();
27802         }
27803         var ps = Roo.Resizable.positions;
27804         for(var k in ps){
27805             if(typeof ps[k] != "function" && this[ps[k]]){
27806                 var h = this[ps[k]];
27807                 h.el.removeAllListeners();
27808                 h.el.remove();
27809             }
27810         }
27811         if(removeEl){
27812             this.el.update("");
27813             this.el.remove();
27814         }
27815     }
27816 });
27817
27818 // private
27819 // hash to map config positions to true positions
27820 Roo.Resizable.positions = {
27821     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27822 };
27823
27824 // private
27825 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27826     if(!this.tpl){
27827         // only initialize the template if resizable is used
27828         var tpl = Roo.DomHelper.createTemplate(
27829             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27830         );
27831         tpl.compile();
27832         Roo.Resizable.Handle.prototype.tpl = tpl;
27833     }
27834     this.position = pos;
27835     this.rz = rz;
27836     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27837     this.el.unselectable();
27838     if(transparent){
27839         this.el.setOpacity(0);
27840     }
27841     this.el.on("mousedown", this.onMouseDown, this);
27842     if(!disableTrackOver){
27843         this.el.on("mouseover", this.onMouseOver, this);
27844         this.el.on("mouseout", this.onMouseOut, this);
27845     }
27846 };
27847
27848 // private
27849 Roo.Resizable.Handle.prototype = {
27850     afterResize : function(rz){
27851         // do nothing
27852     },
27853     // private
27854     onMouseDown : function(e){
27855         this.rz.onMouseDown(this, e);
27856     },
27857     // private
27858     onMouseOver : function(e){
27859         this.rz.handleOver(this, e);
27860     },
27861     // private
27862     onMouseOut : function(e){
27863         this.rz.handleOut(this, e);
27864     }
27865 };/*
27866  * Based on:
27867  * Ext JS Library 1.1.1
27868  * Copyright(c) 2006-2007, Ext JS, LLC.
27869  *
27870  * Originally Released Under LGPL - original licence link has changed is not relivant.
27871  *
27872  * Fork - LGPL
27873  * <script type="text/javascript">
27874  */
27875
27876 /**
27877  * @class Roo.Editor
27878  * @extends Roo.Component
27879  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27880  * @constructor
27881  * Create a new Editor
27882  * @param {Roo.form.Field} field The Field object (or descendant)
27883  * @param {Object} config The config object
27884  */
27885 Roo.Editor = function(field, config){
27886     Roo.Editor.superclass.constructor.call(this, config);
27887     this.field = field;
27888     this.addEvents({
27889         /**
27890              * @event beforestartedit
27891              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27892              * false from the handler of this event.
27893              * @param {Editor} this
27894              * @param {Roo.Element} boundEl The underlying element bound to this editor
27895              * @param {Mixed} value The field value being set
27896              */
27897         "beforestartedit" : true,
27898         /**
27899              * @event startedit
27900              * Fires when this editor is displayed
27901              * @param {Roo.Element} boundEl The underlying element bound to this editor
27902              * @param {Mixed} value The starting field value
27903              */
27904         "startedit" : true,
27905         /**
27906              * @event beforecomplete
27907              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27908              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27909              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27910              * event will not fire since no edit actually occurred.
27911              * @param {Editor} this
27912              * @param {Mixed} value The current field value
27913              * @param {Mixed} startValue The original field value
27914              */
27915         "beforecomplete" : true,
27916         /**
27917              * @event complete
27918              * Fires after editing is complete and any changed value has been written to the underlying field.
27919              * @param {Editor} this
27920              * @param {Mixed} value The current field value
27921              * @param {Mixed} startValue The original field value
27922              */
27923         "complete" : true,
27924         /**
27925          * @event specialkey
27926          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27927          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27928          * @param {Roo.form.Field} this
27929          * @param {Roo.EventObject} e The event object
27930          */
27931         "specialkey" : true
27932     });
27933 };
27934
27935 Roo.extend(Roo.Editor, Roo.Component, {
27936     /**
27937      * @cfg {Boolean/String} autosize
27938      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27939      * or "height" to adopt the height only (defaults to false)
27940      */
27941     /**
27942      * @cfg {Boolean} revertInvalid
27943      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27944      * validation fails (defaults to true)
27945      */
27946     /**
27947      * @cfg {Boolean} ignoreNoChange
27948      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27949      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27950      * will never be ignored.
27951      */
27952     /**
27953      * @cfg {Boolean} hideEl
27954      * False to keep the bound element visible while the editor is displayed (defaults to true)
27955      */
27956     /**
27957      * @cfg {Mixed} value
27958      * The data value of the underlying field (defaults to "")
27959      */
27960     value : "",
27961     /**
27962      * @cfg {String} alignment
27963      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27964      */
27965     alignment: "c-c?",
27966     /**
27967      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27968      * for bottom-right shadow (defaults to "frame")
27969      */
27970     shadow : "frame",
27971     /**
27972      * @cfg {Boolean} constrain True to constrain the editor to the viewport
27973      */
27974     constrain : false,
27975     /**
27976      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
27977      */
27978     completeOnEnter : false,
27979     /**
27980      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
27981      */
27982     cancelOnEsc : false,
27983     /**
27984      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
27985      */
27986     updateEl : false,
27987
27988     // private
27989     onRender : function(ct, position){
27990         this.el = new Roo.Layer({
27991             shadow: this.shadow,
27992             cls: "x-editor",
27993             parentEl : ct,
27994             shim : this.shim,
27995             shadowOffset:4,
27996             id: this.id,
27997             constrain: this.constrain
27998         });
27999         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28000         if(this.field.msgTarget != 'title'){
28001             this.field.msgTarget = 'qtip';
28002         }
28003         this.field.render(this.el);
28004         if(Roo.isGecko){
28005             this.field.el.dom.setAttribute('autocomplete', 'off');
28006         }
28007         this.field.on("specialkey", this.onSpecialKey, this);
28008         if(this.swallowKeys){
28009             this.field.el.swallowEvent(['keydown','keypress']);
28010         }
28011         this.field.show();
28012         this.field.on("blur", this.onBlur, this);
28013         if(this.field.grow){
28014             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28015         }
28016     },
28017
28018     onSpecialKey : function(field, e){
28019         if(this.completeOnEnter && e.getKey() == e.ENTER){
28020             e.stopEvent();
28021             this.completeEdit();
28022         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28023             this.cancelEdit();
28024         }else{
28025             this.fireEvent('specialkey', field, e);
28026         }
28027     },
28028
28029     /**
28030      * Starts the editing process and shows the editor.
28031      * @param {String/HTMLElement/Element} el The element to edit
28032      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28033       * to the innerHTML of el.
28034      */
28035     startEdit : function(el, value){
28036         if(this.editing){
28037             this.completeEdit();
28038         }
28039         this.boundEl = Roo.get(el);
28040         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28041         if(!this.rendered){
28042             this.render(this.parentEl || document.body);
28043         }
28044         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28045             return;
28046         }
28047         this.startValue = v;
28048         this.field.setValue(v);
28049         if(this.autoSize){
28050             var sz = this.boundEl.getSize();
28051             switch(this.autoSize){
28052                 case "width":
28053                 this.setSize(sz.width,  "");
28054                 break;
28055                 case "height":
28056                 this.setSize("",  sz.height);
28057                 break;
28058                 default:
28059                 this.setSize(sz.width,  sz.height);
28060             }
28061         }
28062         this.el.alignTo(this.boundEl, this.alignment);
28063         this.editing = true;
28064         if(Roo.QuickTips){
28065             Roo.QuickTips.disable();
28066         }
28067         this.show();
28068     },
28069
28070     /**
28071      * Sets the height and width of this editor.
28072      * @param {Number} width The new width
28073      * @param {Number} height The new height
28074      */
28075     setSize : function(w, h){
28076         this.field.setSize(w, h);
28077         if(this.el){
28078             this.el.sync();
28079         }
28080     },
28081
28082     /**
28083      * Realigns the editor to the bound field based on the current alignment config value.
28084      */
28085     realign : function(){
28086         this.el.alignTo(this.boundEl, this.alignment);
28087     },
28088
28089     /**
28090      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28091      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28092      */
28093     completeEdit : function(remainVisible){
28094         if(!this.editing){
28095             return;
28096         }
28097         var v = this.getValue();
28098         if(this.revertInvalid !== false && !this.field.isValid()){
28099             v = this.startValue;
28100             this.cancelEdit(true);
28101         }
28102         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28103             this.editing = false;
28104             this.hide();
28105             return;
28106         }
28107         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28108             this.editing = false;
28109             if(this.updateEl && this.boundEl){
28110                 this.boundEl.update(v);
28111             }
28112             if(remainVisible !== true){
28113                 this.hide();
28114             }
28115             this.fireEvent("complete", this, v, this.startValue);
28116         }
28117     },
28118
28119     // private
28120     onShow : function(){
28121         this.el.show();
28122         if(this.hideEl !== false){
28123             this.boundEl.hide();
28124         }
28125         this.field.show();
28126         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28127             this.fixIEFocus = true;
28128             this.deferredFocus.defer(50, this);
28129         }else{
28130             this.field.focus();
28131         }
28132         this.fireEvent("startedit", this.boundEl, this.startValue);
28133     },
28134
28135     deferredFocus : function(){
28136         if(this.editing){
28137             this.field.focus();
28138         }
28139     },
28140
28141     /**
28142      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28143      * reverted to the original starting value.
28144      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28145      * cancel (defaults to false)
28146      */
28147     cancelEdit : function(remainVisible){
28148         if(this.editing){
28149             this.setValue(this.startValue);
28150             if(remainVisible !== true){
28151                 this.hide();
28152             }
28153         }
28154     },
28155
28156     // private
28157     onBlur : function(){
28158         if(this.allowBlur !== true && this.editing){
28159             this.completeEdit();
28160         }
28161     },
28162
28163     // private
28164     onHide : function(){
28165         if(this.editing){
28166             this.completeEdit();
28167             return;
28168         }
28169         this.field.blur();
28170         if(this.field.collapse){
28171             this.field.collapse();
28172         }
28173         this.el.hide();
28174         if(this.hideEl !== false){
28175             this.boundEl.show();
28176         }
28177         if(Roo.QuickTips){
28178             Roo.QuickTips.enable();
28179         }
28180     },
28181
28182     /**
28183      * Sets the data value of the editor
28184      * @param {Mixed} value Any valid value supported by the underlying field
28185      */
28186     setValue : function(v){
28187         this.field.setValue(v);
28188     },
28189
28190     /**
28191      * Gets the data value of the editor
28192      * @return {Mixed} The data value
28193      */
28194     getValue : function(){
28195         return this.field.getValue();
28196     }
28197 });/*
28198  * Based on:
28199  * Ext JS Library 1.1.1
28200  * Copyright(c) 2006-2007, Ext JS, LLC.
28201  *
28202  * Originally Released Under LGPL - original licence link has changed is not relivant.
28203  *
28204  * Fork - LGPL
28205  * <script type="text/javascript">
28206  */
28207  
28208 /**
28209  * @class Roo.BasicDialog
28210  * @extends Roo.util.Observable
28211  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28212  * <pre><code>
28213 var dlg = new Roo.BasicDialog("my-dlg", {
28214     height: 200,
28215     width: 300,
28216     minHeight: 100,
28217     minWidth: 150,
28218     modal: true,
28219     proxyDrag: true,
28220     shadow: true
28221 });
28222 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28223 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28224 dlg.addButton('Cancel', dlg.hide, dlg);
28225 dlg.show();
28226 </code></pre>
28227   <b>A Dialog should always be a direct child of the body element.</b>
28228  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28229  * @cfg {String} title Default text to display in the title bar (defaults to null)
28230  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28231  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28232  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28233  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28234  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28235  * (defaults to null with no animation)
28236  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28237  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28238  * property for valid values (defaults to 'all')
28239  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28240  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28241  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28242  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28243  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28244  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28245  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28246  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28247  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28248  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28249  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28250  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28251  * draggable = true (defaults to false)
28252  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28253  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28254  * shadow (defaults to false)
28255  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28256  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28257  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28258  * @cfg {Array} buttons Array of buttons
28259  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28260  * @constructor
28261  * Create a new BasicDialog.
28262  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28263  * @param {Object} config Configuration options
28264  */
28265 Roo.BasicDialog = function(el, config){
28266     this.el = Roo.get(el);
28267     var dh = Roo.DomHelper;
28268     if(!this.el && config && config.autoCreate){
28269         if(typeof config.autoCreate == "object"){
28270             if(!config.autoCreate.id){
28271                 config.autoCreate.id = el;
28272             }
28273             this.el = dh.append(document.body,
28274                         config.autoCreate, true);
28275         }else{
28276             this.el = dh.append(document.body,
28277                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28278         }
28279     }
28280     el = this.el;
28281     el.setDisplayed(true);
28282     el.hide = this.hideAction;
28283     this.id = el.id;
28284     el.addClass("x-dlg");
28285
28286     Roo.apply(this, config);
28287
28288     this.proxy = el.createProxy("x-dlg-proxy");
28289     this.proxy.hide = this.hideAction;
28290     this.proxy.setOpacity(.5);
28291     this.proxy.hide();
28292
28293     if(config.width){
28294         el.setWidth(config.width);
28295     }
28296     if(config.height){
28297         el.setHeight(config.height);
28298     }
28299     this.size = el.getSize();
28300     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28301         this.xy = [config.x,config.y];
28302     }else{
28303         this.xy = el.getCenterXY(true);
28304     }
28305     /** The header element @type Roo.Element */
28306     this.header = el.child("> .x-dlg-hd");
28307     /** The body element @type Roo.Element */
28308     this.body = el.child("> .x-dlg-bd");
28309     /** The footer element @type Roo.Element */
28310     this.footer = el.child("> .x-dlg-ft");
28311
28312     if(!this.header){
28313         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28314     }
28315     if(!this.body){
28316         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28317     }
28318
28319     this.header.unselectable();
28320     if(this.title){
28321         this.header.update(this.title);
28322     }
28323     // this element allows the dialog to be focused for keyboard event
28324     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28325     this.focusEl.swallowEvent("click", true);
28326
28327     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28328
28329     // wrap the body and footer for special rendering
28330     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28331     if(this.footer){
28332         this.bwrap.dom.appendChild(this.footer.dom);
28333     }
28334
28335     this.bg = this.el.createChild({
28336         tag: "div", cls:"x-dlg-bg",
28337         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28338     });
28339     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28340
28341
28342     if(this.autoScroll !== false && !this.autoTabs){
28343         this.body.setStyle("overflow", "auto");
28344     }
28345
28346     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28347
28348     if(this.closable !== false){
28349         this.el.addClass("x-dlg-closable");
28350         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28351         this.close.on("click", this.closeClick, this);
28352         this.close.addClassOnOver("x-dlg-close-over");
28353     }
28354     if(this.collapsible !== false){
28355         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28356         this.collapseBtn.on("click", this.collapseClick, this);
28357         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28358         this.header.on("dblclick", this.collapseClick, this);
28359     }
28360     if(this.resizable !== false){
28361         this.el.addClass("x-dlg-resizable");
28362         this.resizer = new Roo.Resizable(el, {
28363             minWidth: this.minWidth || 80,
28364             minHeight:this.minHeight || 80,
28365             handles: this.resizeHandles || "all",
28366             pinned: true
28367         });
28368         this.resizer.on("beforeresize", this.beforeResize, this);
28369         this.resizer.on("resize", this.onResize, this);
28370     }
28371     if(this.draggable !== false){
28372         el.addClass("x-dlg-draggable");
28373         if (!this.proxyDrag) {
28374             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28375         }
28376         else {
28377             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28378         }
28379         dd.setHandleElId(this.header.id);
28380         dd.endDrag = this.endMove.createDelegate(this);
28381         dd.startDrag = this.startMove.createDelegate(this);
28382         dd.onDrag = this.onDrag.createDelegate(this);
28383         dd.scroll = false;
28384         this.dd = dd;
28385     }
28386     if(this.modal){
28387         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28388         this.mask.enableDisplayMode("block");
28389         this.mask.hide();
28390         this.el.addClass("x-dlg-modal");
28391     }
28392     if(this.shadow){
28393         this.shadow = new Roo.Shadow({
28394             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28395             offset : this.shadowOffset
28396         });
28397     }else{
28398         this.shadowOffset = 0;
28399     }
28400     if(Roo.useShims && this.shim !== false){
28401         this.shim = this.el.createShim();
28402         this.shim.hide = this.hideAction;
28403         this.shim.hide();
28404     }else{
28405         this.shim = false;
28406     }
28407     if(this.autoTabs){
28408         this.initTabs();
28409     }
28410     if (this.buttons) { 
28411         var bts= this.buttons;
28412         this.buttons = [];
28413         Roo.each(bts, function(b) {
28414             this.addButton(b);
28415         }, this);
28416     }
28417     
28418     
28419     this.addEvents({
28420         /**
28421          * @event keydown
28422          * Fires when a key is pressed
28423          * @param {Roo.BasicDialog} this
28424          * @param {Roo.EventObject} e
28425          */
28426         "keydown" : true,
28427         /**
28428          * @event move
28429          * Fires when this dialog is moved by the user.
28430          * @param {Roo.BasicDialog} this
28431          * @param {Number} x The new page X
28432          * @param {Number} y The new page Y
28433          */
28434         "move" : true,
28435         /**
28436          * @event resize
28437          * Fires when this dialog is resized by the user.
28438          * @param {Roo.BasicDialog} this
28439          * @param {Number} width The new width
28440          * @param {Number} height The new height
28441          */
28442         "resize" : true,
28443         /**
28444          * @event beforehide
28445          * Fires before this dialog is hidden.
28446          * @param {Roo.BasicDialog} this
28447          */
28448         "beforehide" : true,
28449         /**
28450          * @event hide
28451          * Fires when this dialog is hidden.
28452          * @param {Roo.BasicDialog} this
28453          */
28454         "hide" : true,
28455         /**
28456          * @event beforeshow
28457          * Fires before this dialog is shown.
28458          * @param {Roo.BasicDialog} this
28459          */
28460         "beforeshow" : true,
28461         /**
28462          * @event show
28463          * Fires when this dialog is shown.
28464          * @param {Roo.BasicDialog} this
28465          */
28466         "show" : true
28467     });
28468     el.on("keydown", this.onKeyDown, this);
28469     el.on("mousedown", this.toFront, this);
28470     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28471     this.el.hide();
28472     Roo.DialogManager.register(this);
28473     Roo.BasicDialog.superclass.constructor.call(this);
28474 };
28475
28476 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28477     shadowOffset: Roo.isIE ? 6 : 5,
28478     minHeight: 80,
28479     minWidth: 200,
28480     minButtonWidth: 75,
28481     defaultButton: null,
28482     buttonAlign: "right",
28483     tabTag: 'div',
28484     firstShow: true,
28485
28486     /**
28487      * Sets the dialog title text
28488      * @param {String} text The title text to display
28489      * @return {Roo.BasicDialog} this
28490      */
28491     setTitle : function(text){
28492         this.header.update(text);
28493         return this;
28494     },
28495
28496     // private
28497     closeClick : function(){
28498         this.hide();
28499     },
28500
28501     // private
28502     collapseClick : function(){
28503         this[this.collapsed ? "expand" : "collapse"]();
28504     },
28505
28506     /**
28507      * Collapses the dialog to its minimized state (only the title bar is visible).
28508      * Equivalent to the user clicking the collapse dialog button.
28509      */
28510     collapse : function(){
28511         if(!this.collapsed){
28512             this.collapsed = true;
28513             this.el.addClass("x-dlg-collapsed");
28514             this.restoreHeight = this.el.getHeight();
28515             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28516         }
28517     },
28518
28519     /**
28520      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28521      * clicking the expand dialog button.
28522      */
28523     expand : function(){
28524         if(this.collapsed){
28525             this.collapsed = false;
28526             this.el.removeClass("x-dlg-collapsed");
28527             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28528         }
28529     },
28530
28531     /**
28532      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28533      * @return {Roo.TabPanel} The tabs component
28534      */
28535     initTabs : function(){
28536         var tabs = this.getTabs();
28537         while(tabs.getTab(0)){
28538             tabs.removeTab(0);
28539         }
28540         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28541             var dom = el.dom;
28542             tabs.addTab(Roo.id(dom), dom.title);
28543             dom.title = "";
28544         });
28545         tabs.activate(0);
28546         return tabs;
28547     },
28548
28549     // private
28550     beforeResize : function(){
28551         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28552     },
28553
28554     // private
28555     onResize : function(){
28556         this.refreshSize();
28557         this.syncBodyHeight();
28558         this.adjustAssets();
28559         this.focus();
28560         this.fireEvent("resize", this, this.size.width, this.size.height);
28561     },
28562
28563     // private
28564     onKeyDown : function(e){
28565         if(this.isVisible()){
28566             this.fireEvent("keydown", this, e);
28567         }
28568     },
28569
28570     /**
28571      * Resizes the dialog.
28572      * @param {Number} width
28573      * @param {Number} height
28574      * @return {Roo.BasicDialog} this
28575      */
28576     resizeTo : function(width, height){
28577         this.el.setSize(width, height);
28578         this.size = {width: width, height: height};
28579         this.syncBodyHeight();
28580         if(this.fixedcenter){
28581             this.center();
28582         }
28583         if(this.isVisible()){
28584             this.constrainXY();
28585             this.adjustAssets();
28586         }
28587         this.fireEvent("resize", this, width, height);
28588         return this;
28589     },
28590
28591
28592     /**
28593      * Resizes the dialog to fit the specified content size.
28594      * @param {Number} width
28595      * @param {Number} height
28596      * @return {Roo.BasicDialog} this
28597      */
28598     setContentSize : function(w, h){
28599         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28600         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28601         //if(!this.el.isBorderBox()){
28602             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28603             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28604         //}
28605         if(this.tabs){
28606             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28607             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28608         }
28609         this.resizeTo(w, h);
28610         return this;
28611     },
28612
28613     /**
28614      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28615      * executed in response to a particular key being pressed while the dialog is active.
28616      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28617      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28618      * @param {Function} fn The function to call
28619      * @param {Object} scope (optional) The scope of the function
28620      * @return {Roo.BasicDialog} this
28621      */
28622     addKeyListener : function(key, fn, scope){
28623         var keyCode, shift, ctrl, alt;
28624         if(typeof key == "object" && !(key instanceof Array)){
28625             keyCode = key["key"];
28626             shift = key["shift"];
28627             ctrl = key["ctrl"];
28628             alt = key["alt"];
28629         }else{
28630             keyCode = key;
28631         }
28632         var handler = function(dlg, e){
28633             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28634                 var k = e.getKey();
28635                 if(keyCode instanceof Array){
28636                     for(var i = 0, len = keyCode.length; i < len; i++){
28637                         if(keyCode[i] == k){
28638                           fn.call(scope || window, dlg, k, e);
28639                           return;
28640                         }
28641                     }
28642                 }else{
28643                     if(k == keyCode){
28644                         fn.call(scope || window, dlg, k, e);
28645                     }
28646                 }
28647             }
28648         };
28649         this.on("keydown", handler);
28650         return this;
28651     },
28652
28653     /**
28654      * Returns the TabPanel component (creates it if it doesn't exist).
28655      * Note: If you wish to simply check for the existence of tabs without creating them,
28656      * check for a null 'tabs' property.
28657      * @return {Roo.TabPanel} The tabs component
28658      */
28659     getTabs : function(){
28660         if(!this.tabs){
28661             this.el.addClass("x-dlg-auto-tabs");
28662             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28663             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28664         }
28665         return this.tabs;
28666     },
28667
28668     /**
28669      * Adds a button to the footer section of the dialog.
28670      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28671      * object or a valid Roo.DomHelper element config
28672      * @param {Function} handler The function called when the button is clicked
28673      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28674      * @return {Roo.Button} The new button
28675      */
28676     addButton : function(config, handler, scope){
28677         var dh = Roo.DomHelper;
28678         if(!this.footer){
28679             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28680         }
28681         if(!this.btnContainer){
28682             var tb = this.footer.createChild({
28683
28684                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28685                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28686             }, null, true);
28687             this.btnContainer = tb.firstChild.firstChild.firstChild;
28688         }
28689         var bconfig = {
28690             handler: handler,
28691             scope: scope,
28692             minWidth: this.minButtonWidth,
28693             hideParent:true
28694         };
28695         if(typeof config == "string"){
28696             bconfig.text = config;
28697         }else{
28698             if(config.tag){
28699                 bconfig.dhconfig = config;
28700             }else{
28701                 Roo.apply(bconfig, config);
28702             }
28703         }
28704         var fc = false;
28705         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28706             bconfig.position = Math.max(0, bconfig.position);
28707             fc = this.btnContainer.childNodes[bconfig.position];
28708         }
28709          
28710         var btn = new Roo.Button(
28711             fc ? 
28712                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28713                 : this.btnContainer.appendChild(document.createElement("td")),
28714             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28715             bconfig
28716         );
28717         this.syncBodyHeight();
28718         if(!this.buttons){
28719             /**
28720              * Array of all the buttons that have been added to this dialog via addButton
28721              * @type Array
28722              */
28723             this.buttons = [];
28724         }
28725         this.buttons.push(btn);
28726         return btn;
28727     },
28728
28729     /**
28730      * Sets the default button to be focused when the dialog is displayed.
28731      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28732      * @return {Roo.BasicDialog} this
28733      */
28734     setDefaultButton : function(btn){
28735         this.defaultButton = btn;
28736         return this;
28737     },
28738
28739     // private
28740     getHeaderFooterHeight : function(safe){
28741         var height = 0;
28742         if(this.header){
28743            height += this.header.getHeight();
28744         }
28745         if(this.footer){
28746            var fm = this.footer.getMargins();
28747             height += (this.footer.getHeight()+fm.top+fm.bottom);
28748         }
28749         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28750         height += this.centerBg.getPadding("tb");
28751         return height;
28752     },
28753
28754     // private
28755     syncBodyHeight : function(){
28756         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28757         var height = this.size.height - this.getHeaderFooterHeight(false);
28758         bd.setHeight(height-bd.getMargins("tb"));
28759         var hh = this.header.getHeight();
28760         var h = this.size.height-hh;
28761         cb.setHeight(h);
28762         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28763         bw.setHeight(h-cb.getPadding("tb"));
28764         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28765         bd.setWidth(bw.getWidth(true));
28766         if(this.tabs){
28767             this.tabs.syncHeight();
28768             if(Roo.isIE){
28769                 this.tabs.el.repaint();
28770             }
28771         }
28772     },
28773
28774     /**
28775      * Restores the previous state of the dialog if Roo.state is configured.
28776      * @return {Roo.BasicDialog} this
28777      */
28778     restoreState : function(){
28779         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28780         if(box && box.width){
28781             this.xy = [box.x, box.y];
28782             this.resizeTo(box.width, box.height);
28783         }
28784         return this;
28785     },
28786
28787     // private
28788     beforeShow : function(){
28789         this.expand();
28790         if(this.fixedcenter){
28791             this.xy = this.el.getCenterXY(true);
28792         }
28793         if(this.modal){
28794             Roo.get(document.body).addClass("x-body-masked");
28795             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28796             this.mask.show();
28797         }
28798         this.constrainXY();
28799     },
28800
28801     // private
28802     animShow : function(){
28803         var b = Roo.get(this.animateTarget, true).getBox();
28804         this.proxy.setSize(b.width, b.height);
28805         this.proxy.setLocation(b.x, b.y);
28806         this.proxy.show();
28807         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28808                     true, .35, this.showEl.createDelegate(this));
28809     },
28810
28811     /**
28812      * Shows the dialog.
28813      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28814      * @return {Roo.BasicDialog} this
28815      */
28816     show : function(animateTarget){
28817         if (this.fireEvent("beforeshow", this) === false){
28818             return;
28819         }
28820         if(this.syncHeightBeforeShow){
28821             this.syncBodyHeight();
28822         }else if(this.firstShow){
28823             this.firstShow = false;
28824             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28825         }
28826         this.animateTarget = animateTarget || this.animateTarget;
28827         if(!this.el.isVisible()){
28828             this.beforeShow();
28829             if(this.animateTarget){
28830                 this.animShow();
28831             }else{
28832                 this.showEl();
28833             }
28834         }
28835         return this;
28836     },
28837
28838     // private
28839     showEl : function(){
28840         this.proxy.hide();
28841         this.el.setXY(this.xy);
28842         this.el.show();
28843         this.adjustAssets(true);
28844         this.toFront();
28845         this.focus();
28846         // IE peekaboo bug - fix found by Dave Fenwick
28847         if(Roo.isIE){
28848             this.el.repaint();
28849         }
28850         this.fireEvent("show", this);
28851     },
28852
28853     /**
28854      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28855      * dialog itself will receive focus.
28856      */
28857     focus : function(){
28858         if(this.defaultButton){
28859             this.defaultButton.focus();
28860         }else{
28861             this.focusEl.focus();
28862         }
28863     },
28864
28865     // private
28866     constrainXY : function(){
28867         if(this.constraintoviewport !== false){
28868             if(!this.viewSize){
28869                 if(this.container){
28870                     var s = this.container.getSize();
28871                     this.viewSize = [s.width, s.height];
28872                 }else{
28873                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28874                 }
28875             }
28876             var s = Roo.get(this.container||document).getScroll();
28877
28878             var x = this.xy[0], y = this.xy[1];
28879             var w = this.size.width, h = this.size.height;
28880             var vw = this.viewSize[0], vh = this.viewSize[1];
28881             // only move it if it needs it
28882             var moved = false;
28883             // first validate right/bottom
28884             if(x + w > vw+s.left){
28885                 x = vw - w;
28886                 moved = true;
28887             }
28888             if(y + h > vh+s.top){
28889                 y = vh - h;
28890                 moved = true;
28891             }
28892             // then make sure top/left isn't negative
28893             if(x < s.left){
28894                 x = s.left;
28895                 moved = true;
28896             }
28897             if(y < s.top){
28898                 y = s.top;
28899                 moved = true;
28900             }
28901             if(moved){
28902                 // cache xy
28903                 this.xy = [x, y];
28904                 if(this.isVisible()){
28905                     this.el.setLocation(x, y);
28906                     this.adjustAssets();
28907                 }
28908             }
28909         }
28910     },
28911
28912     // private
28913     onDrag : function(){
28914         if(!this.proxyDrag){
28915             this.xy = this.el.getXY();
28916             this.adjustAssets();
28917         }
28918     },
28919
28920     // private
28921     adjustAssets : function(doShow){
28922         var x = this.xy[0], y = this.xy[1];
28923         var w = this.size.width, h = this.size.height;
28924         if(doShow === true){
28925             if(this.shadow){
28926                 this.shadow.show(this.el);
28927             }
28928             if(this.shim){
28929                 this.shim.show();
28930             }
28931         }
28932         if(this.shadow && this.shadow.isVisible()){
28933             this.shadow.show(this.el);
28934         }
28935         if(this.shim && this.shim.isVisible()){
28936             this.shim.setBounds(x, y, w, h);
28937         }
28938     },
28939
28940     // private
28941     adjustViewport : function(w, h){
28942         if(!w || !h){
28943             w = Roo.lib.Dom.getViewWidth();
28944             h = Roo.lib.Dom.getViewHeight();
28945         }
28946         // cache the size
28947         this.viewSize = [w, h];
28948         if(this.modal && this.mask.isVisible()){
28949             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28950             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28951         }
28952         if(this.isVisible()){
28953             this.constrainXY();
28954         }
28955     },
28956
28957     /**
28958      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28959      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28960      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28961      */
28962     destroy : function(removeEl){
28963         if(this.isVisible()){
28964             this.animateTarget = null;
28965             this.hide();
28966         }
28967         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28968         if(this.tabs){
28969             this.tabs.destroy(removeEl);
28970         }
28971         Roo.destroy(
28972              this.shim,
28973              this.proxy,
28974              this.resizer,
28975              this.close,
28976              this.mask
28977         );
28978         if(this.dd){
28979             this.dd.unreg();
28980         }
28981         if(this.buttons){
28982            for(var i = 0, len = this.buttons.length; i < len; i++){
28983                this.buttons[i].destroy();
28984            }
28985         }
28986         this.el.removeAllListeners();
28987         if(removeEl === true){
28988             this.el.update("");
28989             this.el.remove();
28990         }
28991         Roo.DialogManager.unregister(this);
28992     },
28993
28994     // private
28995     startMove : function(){
28996         if(this.proxyDrag){
28997             this.proxy.show();
28998         }
28999         if(this.constraintoviewport !== false){
29000             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29001         }
29002     },
29003
29004     // private
29005     endMove : function(){
29006         if(!this.proxyDrag){
29007             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29008         }else{
29009             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29010             this.proxy.hide();
29011         }
29012         this.refreshSize();
29013         this.adjustAssets();
29014         this.focus();
29015         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29016     },
29017
29018     /**
29019      * Brings this dialog to the front of any other visible dialogs
29020      * @return {Roo.BasicDialog} this
29021      */
29022     toFront : function(){
29023         Roo.DialogManager.bringToFront(this);
29024         return this;
29025     },
29026
29027     /**
29028      * Sends this dialog to the back (under) of any other visible dialogs
29029      * @return {Roo.BasicDialog} this
29030      */
29031     toBack : function(){
29032         Roo.DialogManager.sendToBack(this);
29033         return this;
29034     },
29035
29036     /**
29037      * Centers this dialog in the viewport
29038      * @return {Roo.BasicDialog} this
29039      */
29040     center : function(){
29041         var xy = this.el.getCenterXY(true);
29042         this.moveTo(xy[0], xy[1]);
29043         return this;
29044     },
29045
29046     /**
29047      * Moves the dialog's top-left corner to the specified point
29048      * @param {Number} x
29049      * @param {Number} y
29050      * @return {Roo.BasicDialog} this
29051      */
29052     moveTo : function(x, y){
29053         this.xy = [x,y];
29054         if(this.isVisible()){
29055             this.el.setXY(this.xy);
29056             this.adjustAssets();
29057         }
29058         return this;
29059     },
29060
29061     /**
29062      * Aligns the dialog to the specified element
29063      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29064      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29065      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29066      * @return {Roo.BasicDialog} this
29067      */
29068     alignTo : function(element, position, offsets){
29069         this.xy = this.el.getAlignToXY(element, position, offsets);
29070         if(this.isVisible()){
29071             this.el.setXY(this.xy);
29072             this.adjustAssets();
29073         }
29074         return this;
29075     },
29076
29077     /**
29078      * Anchors an element to another element and realigns it when the window is resized.
29079      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29080      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29081      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29082      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29083      * is a number, it is used as the buffer delay (defaults to 50ms).
29084      * @return {Roo.BasicDialog} this
29085      */
29086     anchorTo : function(el, alignment, offsets, monitorScroll){
29087         var action = function(){
29088             this.alignTo(el, alignment, offsets);
29089         };
29090         Roo.EventManager.onWindowResize(action, this);
29091         var tm = typeof monitorScroll;
29092         if(tm != 'undefined'){
29093             Roo.EventManager.on(window, 'scroll', action, this,
29094                 {buffer: tm == 'number' ? monitorScroll : 50});
29095         }
29096         action.call(this);
29097         return this;
29098     },
29099
29100     /**
29101      * Returns true if the dialog is visible
29102      * @return {Boolean}
29103      */
29104     isVisible : function(){
29105         return this.el.isVisible();
29106     },
29107
29108     // private
29109     animHide : function(callback){
29110         var b = Roo.get(this.animateTarget).getBox();
29111         this.proxy.show();
29112         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29113         this.el.hide();
29114         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29115                     this.hideEl.createDelegate(this, [callback]));
29116     },
29117
29118     /**
29119      * Hides the dialog.
29120      * @param {Function} callback (optional) Function to call when the dialog is hidden
29121      * @return {Roo.BasicDialog} this
29122      */
29123     hide : function(callback){
29124         if (this.fireEvent("beforehide", this) === false){
29125             return;
29126         }
29127         if(this.shadow){
29128             this.shadow.hide();
29129         }
29130         if(this.shim) {
29131           this.shim.hide();
29132         }
29133         if(this.animateTarget){
29134            this.animHide(callback);
29135         }else{
29136             this.el.hide();
29137             this.hideEl(callback);
29138         }
29139         return this;
29140     },
29141
29142     // private
29143     hideEl : function(callback){
29144         this.proxy.hide();
29145         if(this.modal){
29146             this.mask.hide();
29147             Roo.get(document.body).removeClass("x-body-masked");
29148         }
29149         this.fireEvent("hide", this);
29150         if(typeof callback == "function"){
29151             callback();
29152         }
29153     },
29154
29155     // private
29156     hideAction : function(){
29157         this.setLeft("-10000px");
29158         this.setTop("-10000px");
29159         this.setStyle("visibility", "hidden");
29160     },
29161
29162     // private
29163     refreshSize : function(){
29164         this.size = this.el.getSize();
29165         this.xy = this.el.getXY();
29166         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29167     },
29168
29169     // private
29170     // z-index is managed by the DialogManager and may be overwritten at any time
29171     setZIndex : function(index){
29172         if(this.modal){
29173             this.mask.setStyle("z-index", index);
29174         }
29175         if(this.shim){
29176             this.shim.setStyle("z-index", ++index);
29177         }
29178         if(this.shadow){
29179             this.shadow.setZIndex(++index);
29180         }
29181         this.el.setStyle("z-index", ++index);
29182         if(this.proxy){
29183             this.proxy.setStyle("z-index", ++index);
29184         }
29185         if(this.resizer){
29186             this.resizer.proxy.setStyle("z-index", ++index);
29187         }
29188
29189         this.lastZIndex = index;
29190     },
29191
29192     /**
29193      * Returns the element for this dialog
29194      * @return {Roo.Element} The underlying dialog Element
29195      */
29196     getEl : function(){
29197         return this.el;
29198     }
29199 });
29200
29201 /**
29202  * @class Roo.DialogManager
29203  * Provides global access to BasicDialogs that have been created and
29204  * support for z-indexing (layering) multiple open dialogs.
29205  */
29206 Roo.DialogManager = function(){
29207     var list = {};
29208     var accessList = [];
29209     var front = null;
29210
29211     // private
29212     var sortDialogs = function(d1, d2){
29213         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29214     };
29215
29216     // private
29217     var orderDialogs = function(){
29218         accessList.sort(sortDialogs);
29219         var seed = Roo.DialogManager.zseed;
29220         for(var i = 0, len = accessList.length; i < len; i++){
29221             var dlg = accessList[i];
29222             if(dlg){
29223                 dlg.setZIndex(seed + (i*10));
29224             }
29225         }
29226     };
29227
29228     return {
29229         /**
29230          * The starting z-index for BasicDialogs (defaults to 9000)
29231          * @type Number The z-index value
29232          */
29233         zseed : 9000,
29234
29235         // private
29236         register : function(dlg){
29237             list[dlg.id] = dlg;
29238             accessList.push(dlg);
29239         },
29240
29241         // private
29242         unregister : function(dlg){
29243             delete list[dlg.id];
29244             var i=0;
29245             var len=0;
29246             if(!accessList.indexOf){
29247                 for(  i = 0, len = accessList.length; i < len; i++){
29248                     if(accessList[i] == dlg){
29249                         accessList.splice(i, 1);
29250                         return;
29251                     }
29252                 }
29253             }else{
29254                  i = accessList.indexOf(dlg);
29255                 if(i != -1){
29256                     accessList.splice(i, 1);
29257                 }
29258             }
29259         },
29260
29261         /**
29262          * Gets a registered dialog by id
29263          * @param {String/Object} id The id of the dialog or a dialog
29264          * @return {Roo.BasicDialog} this
29265          */
29266         get : function(id){
29267             return typeof id == "object" ? id : list[id];
29268         },
29269
29270         /**
29271          * Brings the specified dialog to the front
29272          * @param {String/Object} dlg The id of the dialog or a dialog
29273          * @return {Roo.BasicDialog} this
29274          */
29275         bringToFront : function(dlg){
29276             dlg = this.get(dlg);
29277             if(dlg != front){
29278                 front = dlg;
29279                 dlg._lastAccess = new Date().getTime();
29280                 orderDialogs();
29281             }
29282             return dlg;
29283         },
29284
29285         /**
29286          * Sends the specified dialog to the back
29287          * @param {String/Object} dlg The id of the dialog or a dialog
29288          * @return {Roo.BasicDialog} this
29289          */
29290         sendToBack : function(dlg){
29291             dlg = this.get(dlg);
29292             dlg._lastAccess = -(new Date().getTime());
29293             orderDialogs();
29294             return dlg;
29295         },
29296
29297         /**
29298          * Hides all dialogs
29299          */
29300         hideAll : function(){
29301             for(var id in list){
29302                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29303                     list[id].hide();
29304                 }
29305             }
29306         }
29307     };
29308 }();
29309
29310 /**
29311  * @class Roo.LayoutDialog
29312  * @extends Roo.BasicDialog
29313  * Dialog which provides adjustments for working with a layout in a Dialog.
29314  * Add your necessary layout config options to the dialog's config.<br>
29315  * Example usage (including a nested layout):
29316  * <pre><code>
29317 if(!dialog){
29318     dialog = new Roo.LayoutDialog("download-dlg", {
29319         modal: true,
29320         width:600,
29321         height:450,
29322         shadow:true,
29323         minWidth:500,
29324         minHeight:350,
29325         autoTabs:true,
29326         proxyDrag:true,
29327         // layout config merges with the dialog config
29328         center:{
29329             tabPosition: "top",
29330             alwaysShowTabs: true
29331         }
29332     });
29333     dialog.addKeyListener(27, dialog.hide, dialog);
29334     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29335     dialog.addButton("Build It!", this.getDownload, this);
29336
29337     // we can even add nested layouts
29338     var innerLayout = new Roo.BorderLayout("dl-inner", {
29339         east: {
29340             initialSize: 200,
29341             autoScroll:true,
29342             split:true
29343         },
29344         center: {
29345             autoScroll:true
29346         }
29347     });
29348     innerLayout.beginUpdate();
29349     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29350     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29351     innerLayout.endUpdate(true);
29352
29353     var layout = dialog.getLayout();
29354     layout.beginUpdate();
29355     layout.add("center", new Roo.ContentPanel("standard-panel",
29356                         {title: "Download the Source", fitToFrame:true}));
29357     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29358                {title: "Build your own roo.js"}));
29359     layout.getRegion("center").showPanel(sp);
29360     layout.endUpdate();
29361 }
29362 </code></pre>
29363     * @constructor
29364     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29365     * @param {Object} config configuration options
29366   */
29367 Roo.LayoutDialog = function(el, cfg){
29368     
29369     var config=  cfg;
29370     if (typeof(cfg) == 'undefined') {
29371         config = Roo.apply({}, el);
29372         el = Roo.get( document.documentElement || document.body).createChild();
29373         //config.autoCreate = true;
29374     }
29375     
29376     
29377     config.autoTabs = false;
29378     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29379     this.body.setStyle({overflow:"hidden", position:"relative"});
29380     this.layout = new Roo.BorderLayout(this.body.dom, config);
29381     this.layout.monitorWindowResize = false;
29382     this.el.addClass("x-dlg-auto-layout");
29383     // fix case when center region overwrites center function
29384     this.center = Roo.BasicDialog.prototype.center;
29385     this.on("show", this.layout.layout, this.layout, true);
29386     if (config.items) {
29387         var xitems = config.items;
29388         delete config.items;
29389         Roo.each(xitems, this.addxtype, this);
29390     }
29391     
29392     
29393 };
29394 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29395     /**
29396      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29397      * @deprecated
29398      */
29399     endUpdate : function(){
29400         this.layout.endUpdate();
29401     },
29402
29403     /**
29404      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29405      *  @deprecated
29406      */
29407     beginUpdate : function(){
29408         this.layout.beginUpdate();
29409     },
29410
29411     /**
29412      * Get the BorderLayout for this dialog
29413      * @return {Roo.BorderLayout}
29414      */
29415     getLayout : function(){
29416         return this.layout;
29417     },
29418
29419     showEl : function(){
29420         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29421         if(Roo.isIE7){
29422             this.layout.layout();
29423         }
29424     },
29425
29426     // private
29427     // Use the syncHeightBeforeShow config option to control this automatically
29428     syncBodyHeight : function(){
29429         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29430         if(this.layout){this.layout.layout();}
29431     },
29432     
29433       /**
29434      * Add an xtype element (actually adds to the layout.)
29435      * @return {Object} xdata xtype object data.
29436      */
29437     
29438     addxtype : function(c) {
29439         return this.layout.addxtype(c);
29440     }
29441 });