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 /*, thisp*/)
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 if (!Array.prototype.map)
873 {
874   Array.prototype.
875 }/*
876  * Based on:
877  * Ext JS Library 1.1.1
878  * Copyright(c) 2006-2007, Ext JS, LLC.
879  *
880  * Originally Released Under LGPL - original licence link has changed is not relivant.
881  *
882  * Fork - LGPL
883  * <script type="text/javascript">
884  */
885
886 /**
887  * @class Date
888  *
889  * The date parsing and format syntax is a subset of
890  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
891  * supported will provide results equivalent to their PHP versions.
892  *
893  * Following is the list of all currently supported formats:
894  *<pre>
895 Sample date:
896 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
897
898 Format  Output      Description
899 ------  ----------  --------------------------------------------------------------
900   d      10         Day of the month, 2 digits with leading zeros
901   D      Wed        A textual representation of a day, three letters
902   j      10         Day of the month without leading zeros
903   l      Wednesday  A full textual representation of the day of the week
904   S      th         English ordinal day of month suffix, 2 chars (use with j)
905   w      3          Numeric representation of the day of the week
906   z      9          The julian date, or day of the year (0-365)
907   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
908   F      January    A full textual representation of the month
909   m      01         Numeric representation of a month, with leading zeros
910   M      Jan        Month name abbreviation, three letters
911   n      1          Numeric representation of a month, without leading zeros
912   t      31         Number of days in the given month
913   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
914   Y      2007       A full numeric representation of a year, 4 digits
915   y      07         A two digit representation of a year
916   a      pm         Lowercase Ante meridiem and Post meridiem
917   A      PM         Uppercase Ante meridiem and Post meridiem
918   g      3          12-hour format of an hour without leading zeros
919   G      15         24-hour format of an hour without leading zeros
920   h      03         12-hour format of an hour with leading zeros
921   H      15         24-hour format of an hour with leading zeros
922   i      05         Minutes with leading zeros
923   s      01         Seconds, with leading zeros
924   O      -0600      Difference to Greenwich time (GMT) in hours
925   T      CST        Timezone setting of the machine running the code
926   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
927 </pre>
928  *
929  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
930  * <pre><code>
931 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
932 document.write(dt.format('Y-m-d'));                         //2007-01-10
933 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
934 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
935  </code></pre>
936  *
937  * Here are some standard date/time patterns that you might find helpful.  They
938  * are not part of the source of Date.js, but to use them you can simply copy this
939  * block of code into any script that is included after Date.js and they will also become
940  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
941  * <pre><code>
942 Date.patterns = {
943     ISO8601Long:"Y-m-d H:i:s",
944     ISO8601Short:"Y-m-d",
945     ShortDate: "n/j/Y",
946     LongDate: "l, F d, Y",
947     FullDateTime: "l, F d, Y g:i:s A",
948     MonthDay: "F d",
949     ShortTime: "g:i A",
950     LongTime: "g:i:s A",
951     SortableDateTime: "Y-m-d\\TH:i:s",
952     UniversalSortableDateTime: "Y-m-d H:i:sO",
953     YearMonth: "F, Y"
954 };
955 </code></pre>
956  *
957  * Example usage:
958  * <pre><code>
959 var dt = new Date();
960 document.write(dt.format(Date.patterns.ShortDate));
961  </code></pre>
962  */
963
964 /*
965  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
966  * They generate precompiled functions from date formats instead of parsing and
967  * processing the pattern every time you format a date.  These functions are available
968  * on every Date object (any javascript function).
969  *
970  * The original article and download are here:
971  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
972  *
973  */
974  
975  
976  // was in core
977 /**
978  Returns the number of milliseconds between this date and date
979  @param {Date} date (optional) Defaults to now
980  @return {Number} The diff in milliseconds
981  @member Date getElapsed
982  */
983 Date.prototype.getElapsed = function(date) {
984         return Math.abs((date || new Date()).getTime()-this.getTime());
985 };
986 // was in date file..
987
988
989 // private
990 Date.parseFunctions = {count:0};
991 // private
992 Date.parseRegexes = [];
993 // private
994 Date.formatFunctions = {count:0};
995
996 // private
997 Date.prototype.dateFormat = function(format) {
998     if (Date.formatFunctions[format] == null) {
999         Date.createNewFormat(format);
1000     }
1001     var func = Date.formatFunctions[format];
1002     return this[func]();
1003 };
1004
1005
1006 /**
1007  * Formats a date given the supplied format string
1008  * @param {String} format The format string
1009  * @return {String} The formatted date
1010  * @method
1011  */
1012 Date.prototype.format = Date.prototype.dateFormat;
1013
1014 // private
1015 Date.createNewFormat = function(format) {
1016     var funcName = "format" + Date.formatFunctions.count++;
1017     Date.formatFunctions[format] = funcName;
1018     var code = "Date.prototype." + funcName + " = function(){return ";
1019     var special = false;
1020     var ch = '';
1021     for (var i = 0; i < format.length; ++i) {
1022         ch = format.charAt(i);
1023         if (!special && ch == "\\") {
1024             special = true;
1025         }
1026         else if (special) {
1027             special = false;
1028             code += "'" + String.escape(ch) + "' + ";
1029         }
1030         else {
1031             code += Date.getFormatCode(ch);
1032         }
1033     }
1034     /** eval:var:zzzzzzzzzzzzz */
1035     eval(code.substring(0, code.length - 3) + ";}");
1036 };
1037
1038 // private
1039 Date.getFormatCode = function(character) {
1040     switch (character) {
1041     case "d":
1042         return "String.leftPad(this.getDate(), 2, '0') + ";
1043     case "D":
1044         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1045     case "j":
1046         return "this.getDate() + ";
1047     case "l":
1048         return "Date.dayNames[this.getDay()] + ";
1049     case "S":
1050         return "this.getSuffix() + ";
1051     case "w":
1052         return "this.getDay() + ";
1053     case "z":
1054         return "this.getDayOfYear() + ";
1055     case "W":
1056         return "this.getWeekOfYear() + ";
1057     case "F":
1058         return "Date.monthNames[this.getMonth()] + ";
1059     case "m":
1060         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1061     case "M":
1062         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1063     case "n":
1064         return "(this.getMonth() + 1) + ";
1065     case "t":
1066         return "this.getDaysInMonth() + ";
1067     case "L":
1068         return "(this.isLeapYear() ? 1 : 0) + ";
1069     case "Y":
1070         return "this.getFullYear() + ";
1071     case "y":
1072         return "('' + this.getFullYear()).substring(2, 4) + ";
1073     case "a":
1074         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1075     case "A":
1076         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1077     case "g":
1078         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1079     case "G":
1080         return "this.getHours() + ";
1081     case "h":
1082         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1083     case "H":
1084         return "String.leftPad(this.getHours(), 2, '0') + ";
1085     case "i":
1086         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1087     case "s":
1088         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1089     case "O":
1090         return "this.getGMTOffset() + ";
1091     case "T":
1092         return "this.getTimezone() + ";
1093     case "Z":
1094         return "(this.getTimezoneOffset() * -60) + ";
1095     default:
1096         return "'" + String.escape(character) + "' + ";
1097     }
1098 };
1099
1100 /**
1101  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1102  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1103  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1104  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1105  * string or the parse operation will fail.
1106  * Example Usage:
1107 <pre><code>
1108 //dt = Fri May 25 2007 (current date)
1109 var dt = new Date();
1110
1111 //dt = Thu May 25 2006 (today's month/day in 2006)
1112 dt = Date.parseDate("2006", "Y");
1113
1114 //dt = Sun Jan 15 2006 (all date parts specified)
1115 dt = Date.parseDate("2006-1-15", "Y-m-d");
1116
1117 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1118 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1119 </code></pre>
1120  * @param {String} input The unparsed date as a string
1121  * @param {String} format The format the date is in
1122  * @return {Date} The parsed date
1123  * @static
1124  */
1125 Date.parseDate = function(input, format) {
1126     if (Date.parseFunctions[format] == null) {
1127         Date.createParser(format);
1128     }
1129     var func = Date.parseFunctions[format];
1130     return Date[func](input);
1131 };
1132 /**
1133  * @private
1134  */
1135 Date.createParser = function(format) {
1136     var funcName = "parse" + Date.parseFunctions.count++;
1137     var regexNum = Date.parseRegexes.length;
1138     var currentGroup = 1;
1139     Date.parseFunctions[format] = funcName;
1140
1141     var code = "Date." + funcName + " = function(input){\n"
1142         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1143         + "var d = new Date();\n"
1144         + "y = d.getFullYear();\n"
1145         + "m = d.getMonth();\n"
1146         + "d = d.getDate();\n"
1147         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1148         + "if (results && results.length > 0) {";
1149     var regex = "";
1150
1151     var special = false;
1152     var ch = '';
1153     for (var i = 0; i < format.length; ++i) {
1154         ch = format.charAt(i);
1155         if (!special && ch == "\\") {
1156             special = true;
1157         }
1158         else if (special) {
1159             special = false;
1160             regex += String.escape(ch);
1161         }
1162         else {
1163             var obj = Date.formatCodeToRegex(ch, currentGroup);
1164             currentGroup += obj.g;
1165             regex += obj.s;
1166             if (obj.g && obj.c) {
1167                 code += obj.c;
1168             }
1169         }
1170     }
1171
1172     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1173         + "{v = new Date(y, m, d, h, i, s);}\n"
1174         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1175         + "{v = new Date(y, m, d, h, i);}\n"
1176         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1177         + "{v = new Date(y, m, d, h);}\n"
1178         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1179         + "{v = new Date(y, m, d);}\n"
1180         + "else if (y >= 0 && m >= 0)\n"
1181         + "{v = new Date(y, m);}\n"
1182         + "else if (y >= 0)\n"
1183         + "{v = new Date(y);}\n"
1184         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1185         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1186         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1187         + ";}";
1188
1189     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1190     /** eval:var:zzzzzzzzzzzzz */
1191     eval(code);
1192 };
1193
1194 // private
1195 Date.formatCodeToRegex = function(character, currentGroup) {
1196     switch (character) {
1197     case "D":
1198         return {g:0,
1199         c:null,
1200         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1201     case "j":
1202         return {g:1,
1203             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1204             s:"(\\d{1,2})"}; // day of month without leading zeroes
1205     case "d":
1206         return {g:1,
1207             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1208             s:"(\\d{2})"}; // day of month with leading zeroes
1209     case "l":
1210         return {g:0,
1211             c:null,
1212             s:"(?:" + Date.dayNames.join("|") + ")"};
1213     case "S":
1214         return {g:0,
1215             c:null,
1216             s:"(?:st|nd|rd|th)"};
1217     case "w":
1218         return {g:0,
1219             c:null,
1220             s:"\\d"};
1221     case "z":
1222         return {g:0,
1223             c:null,
1224             s:"(?:\\d{1,3})"};
1225     case "W":
1226         return {g:0,
1227             c:null,
1228             s:"(?:\\d{2})"};
1229     case "F":
1230         return {g:1,
1231             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1232             s:"(" + Date.monthNames.join("|") + ")"};
1233     case "M":
1234         return {g:1,
1235             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1236             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1237     case "n":
1238         return {g:1,
1239             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1240             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1241     case "m":
1242         return {g:1,
1243             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1244             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1245     case "t":
1246         return {g:0,
1247             c:null,
1248             s:"\\d{1,2}"};
1249     case "L":
1250         return {g:0,
1251             c:null,
1252             s:"(?:1|0)"};
1253     case "Y":
1254         return {g:1,
1255             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1256             s:"(\\d{4})"};
1257     case "y":
1258         return {g:1,
1259             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1260                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1261             s:"(\\d{1,2})"};
1262     case "a":
1263         return {g:1,
1264             c:"if (results[" + currentGroup + "] == 'am') {\n"
1265                 + "if (h == 12) { h = 0; }\n"
1266                 + "} else { if (h < 12) { h += 12; }}",
1267             s:"(am|pm)"};
1268     case "A":
1269         return {g:1,
1270             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1271                 + "if (h == 12) { h = 0; }\n"
1272                 + "} else { if (h < 12) { h += 12; }}",
1273             s:"(AM|PM)"};
1274     case "g":
1275     case "G":
1276         return {g:1,
1277             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1278             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1279     case "h":
1280     case "H":
1281         return {g:1,
1282             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1283             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1284     case "i":
1285         return {g:1,
1286             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{2})"};
1288     case "s":
1289         return {g:1,
1290             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1291             s:"(\\d{2})"};
1292     case "O":
1293         return {g:1,
1294             c:[
1295                 "o = results[", currentGroup, "];\n",
1296                 "var sn = o.substring(0,1);\n", // get + / - sign
1297                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1298                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1299                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1300                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1301             ].join(""),
1302             s:"([+\-]\\d{4})"};
1303     case "T":
1304         return {g:0,
1305             c:null,
1306             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1307     case "Z":
1308         return {g:1,
1309             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1310                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1311             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1312     default:
1313         return {g:0,
1314             c:null,
1315             s:String.escape(character)};
1316     }
1317 };
1318
1319 /**
1320  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1321  * @return {String} The abbreviated timezone name (e.g. 'CST')
1322  */
1323 Date.prototype.getTimezone = function() {
1324     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1325 };
1326
1327 /**
1328  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1329  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1330  */
1331 Date.prototype.getGMTOffset = function() {
1332     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1333         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1334         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1335 };
1336
1337 /**
1338  * Get the numeric day number of the year, adjusted for leap year.
1339  * @return {Number} 0 through 364 (365 in leap years)
1340  */
1341 Date.prototype.getDayOfYear = function() {
1342     var num = 0;
1343     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1344     for (var i = 0; i < this.getMonth(); ++i) {
1345         num += Date.daysInMonth[i];
1346     }
1347     return num + this.getDate() - 1;
1348 };
1349
1350 /**
1351  * Get the string representation of the numeric week number of the year
1352  * (equivalent to the format specifier 'W').
1353  * @return {String} '00' through '52'
1354  */
1355 Date.prototype.getWeekOfYear = function() {
1356     // Skip to Thursday of this week
1357     var now = this.getDayOfYear() + (4 - this.getDay());
1358     // Find the first Thursday of the year
1359     var jan1 = new Date(this.getFullYear(), 0, 1);
1360     var then = (7 - jan1.getDay() + 4);
1361     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1362 };
1363
1364 /**
1365  * Whether or not the current date is in a leap year.
1366  * @return {Boolean} True if the current date is in a leap year, else false
1367  */
1368 Date.prototype.isLeapYear = function() {
1369     var year = this.getFullYear();
1370     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1371 };
1372
1373 /**
1374  * Get the first day of the current month, adjusted for leap year.  The returned value
1375  * is the numeric day index within the week (0-6) which can be used in conjunction with
1376  * the {@link #monthNames} array to retrieve the textual day name.
1377  * Example:
1378  *<pre><code>
1379 var dt = new Date('1/10/2007');
1380 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1381 </code></pre>
1382  * @return {Number} The day number (0-6)
1383  */
1384 Date.prototype.getFirstDayOfMonth = function() {
1385     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1386     return (day < 0) ? (day + 7) : day;
1387 };
1388
1389 /**
1390  * Get the last day of the current month, adjusted for leap year.  The returned value
1391  * is the numeric day index within the week (0-6) which can be used in conjunction with
1392  * the {@link #monthNames} array to retrieve the textual day name.
1393  * Example:
1394  *<pre><code>
1395 var dt = new Date('1/10/2007');
1396 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1397 </code></pre>
1398  * @return {Number} The day number (0-6)
1399  */
1400 Date.prototype.getLastDayOfMonth = function() {
1401     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1402     return (day < 0) ? (day + 7) : day;
1403 };
1404
1405
1406 /**
1407  * Get the first date of this date's month
1408  * @return {Date}
1409  */
1410 Date.prototype.getFirstDateOfMonth = function() {
1411     return new Date(this.getFullYear(), this.getMonth(), 1);
1412 };
1413
1414 /**
1415  * Get the last date of this date's month
1416  * @return {Date}
1417  */
1418 Date.prototype.getLastDateOfMonth = function() {
1419     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1420 };
1421 /**
1422  * Get the number of days in the current month, adjusted for leap year.
1423  * @return {Number} The number of days in the month
1424  */
1425 Date.prototype.getDaysInMonth = function() {
1426     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1427     return Date.daysInMonth[this.getMonth()];
1428 };
1429
1430 /**
1431  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1432  * @return {String} 'st, 'nd', 'rd' or 'th'
1433  */
1434 Date.prototype.getSuffix = function() {
1435     switch (this.getDate()) {
1436         case 1:
1437         case 21:
1438         case 31:
1439             return "st";
1440         case 2:
1441         case 22:
1442             return "nd";
1443         case 3:
1444         case 23:
1445             return "rd";
1446         default:
1447             return "th";
1448     }
1449 };
1450
1451 // private
1452 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1453
1454 /**
1455  * An array of textual month names.
1456  * Override these values for international dates, for example...
1457  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1458  * @type Array
1459  * @static
1460  */
1461 Date.monthNames =
1462    ["January",
1463     "February",
1464     "March",
1465     "April",
1466     "May",
1467     "June",
1468     "July",
1469     "August",
1470     "September",
1471     "October",
1472     "November",
1473     "December"];
1474
1475 /**
1476  * An array of textual day names.
1477  * Override these values for international dates, for example...
1478  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1479  * @type Array
1480  * @static
1481  */
1482 Date.dayNames =
1483    ["Sunday",
1484     "Monday",
1485     "Tuesday",
1486     "Wednesday",
1487     "Thursday",
1488     "Friday",
1489     "Saturday"];
1490
1491 // private
1492 Date.y2kYear = 50;
1493 // private
1494 Date.monthNumbers = {
1495     Jan:0,
1496     Feb:1,
1497     Mar:2,
1498     Apr:3,
1499     May:4,
1500     Jun:5,
1501     Jul:6,
1502     Aug:7,
1503     Sep:8,
1504     Oct:9,
1505     Nov:10,
1506     Dec:11};
1507
1508 /**
1509  * Creates and returns a new Date instance with the exact same date value as the called instance.
1510  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1511  * variable will also be changed.  When the intention is to create a new variable that will not
1512  * modify the original instance, you should create a clone.
1513  *
1514  * Example of correctly cloning a date:
1515  * <pre><code>
1516 //wrong way:
1517 var orig = new Date('10/1/2006');
1518 var copy = orig;
1519 copy.setDate(5);
1520 document.write(orig);  //returns 'Thu Oct 05 2006'!
1521
1522 //correct way:
1523 var orig = new Date('10/1/2006');
1524 var copy = orig.clone();
1525 copy.setDate(5);
1526 document.write(orig);  //returns 'Thu Oct 01 2006'
1527 </code></pre>
1528  * @return {Date} The new Date instance
1529  */
1530 Date.prototype.clone = function() {
1531         return new Date(this.getTime());
1532 };
1533
1534 /**
1535  * Clears any time information from this date
1536  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1537  @return {Date} this or the clone
1538  */
1539 Date.prototype.clearTime = function(clone){
1540     if(clone){
1541         return this.clone().clearTime();
1542     }
1543     this.setHours(0);
1544     this.setMinutes(0);
1545     this.setSeconds(0);
1546     this.setMilliseconds(0);
1547     return this;
1548 };
1549
1550 // private
1551 // safari setMonth is broken
1552 if(Roo.isSafari){
1553     Date.brokenSetMonth = Date.prototype.setMonth;
1554         Date.prototype.setMonth = function(num){
1555                 if(num <= -1){
1556                         var n = Math.ceil(-num);
1557                         var back_year = Math.ceil(n/12);
1558                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1559                         this.setFullYear(this.getFullYear() - back_year);
1560                         return Date.brokenSetMonth.call(this, month);
1561                 } else {
1562                         return Date.brokenSetMonth.apply(this, arguments);
1563                 }
1564         };
1565 }
1566
1567 /** Date interval constant 
1568 * @static 
1569 * @type String */
1570 Date.MILLI = "ms";
1571 /** Date interval constant 
1572 * @static 
1573 * @type String */
1574 Date.SECOND = "s";
1575 /** Date interval constant 
1576 * @static 
1577 * @type String */
1578 Date.MINUTE = "mi";
1579 /** Date interval constant 
1580 * @static 
1581 * @type String */
1582 Date.HOUR = "h";
1583 /** Date interval constant 
1584 * @static 
1585 * @type String */
1586 Date.DAY = "d";
1587 /** Date interval constant 
1588 * @static 
1589 * @type String */
1590 Date.MONTH = "mo";
1591 /** Date interval constant 
1592 * @static 
1593 * @type String */
1594 Date.YEAR = "y";
1595
1596 /**
1597  * Provides a convenient method of performing basic date arithmetic.  This method
1598  * does not modify the Date instance being called - it creates and returns
1599  * a new Date instance containing the resulting date value.
1600  *
1601  * Examples:
1602  * <pre><code>
1603 //Basic usage:
1604 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1605 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1606
1607 //Negative values will subtract correctly:
1608 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1609 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1610
1611 //You can even chain several calls together in one line!
1612 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1613 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1614  </code></pre>
1615  *
1616  * @param {String} interval   A valid date interval enum value
1617  * @param {Number} value      The amount to add to the current date
1618  * @return {Date} The new Date instance
1619  */
1620 Date.prototype.add = function(interval, value){
1621   var d = this.clone();
1622   if (!interval || value === 0) return d;
1623   switch(interval.toLowerCase()){
1624     case Date.MILLI:
1625       d.setMilliseconds(this.getMilliseconds() + value);
1626       break;
1627     case Date.SECOND:
1628       d.setSeconds(this.getSeconds() + value);
1629       break;
1630     case Date.MINUTE:
1631       d.setMinutes(this.getMinutes() + value);
1632       break;
1633     case Date.HOUR:
1634       d.setHours(this.getHours() + value);
1635       break;
1636     case Date.DAY:
1637       d.setDate(this.getDate() + value);
1638       break;
1639     case Date.MONTH:
1640       var day = this.getDate();
1641       if(day > 28){
1642           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1643       }
1644       d.setDate(day);
1645       d.setMonth(this.getMonth() + value);
1646       break;
1647     case Date.YEAR:
1648       d.setFullYear(this.getFullYear() + value);
1649       break;
1650   }
1651   return d;
1652 };/*
1653  * Based on:
1654  * Ext JS Library 1.1.1
1655  * Copyright(c) 2006-2007, Ext JS, LLC.
1656  *
1657  * Originally Released Under LGPL - original licence link has changed is not relivant.
1658  *
1659  * Fork - LGPL
1660  * <script type="text/javascript">
1661  */
1662
1663 Roo.lib.Dom = {
1664     getViewWidth : function(full) {
1665         return full ? this.getDocumentWidth() : this.getViewportWidth();
1666     },
1667
1668     getViewHeight : function(full) {
1669         return full ? this.getDocumentHeight() : this.getViewportHeight();
1670     },
1671
1672     getDocumentHeight: function() {
1673         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1674         return Math.max(scrollHeight, this.getViewportHeight());
1675     },
1676
1677     getDocumentWidth: function() {
1678         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1679         return Math.max(scrollWidth, this.getViewportWidth());
1680     },
1681
1682     getViewportHeight: function() {
1683         var height = self.innerHeight;
1684         var mode = document.compatMode;
1685
1686         if ((mode || Roo.isIE) && !Roo.isOpera) {
1687             height = (mode == "CSS1Compat") ?
1688                      document.documentElement.clientHeight :
1689                      document.body.clientHeight;
1690         }
1691
1692         return height;
1693     },
1694
1695     getViewportWidth: function() {
1696         var width = self.innerWidth;
1697         var mode = document.compatMode;
1698
1699         if (mode || Roo.isIE) {
1700             width = (mode == "CSS1Compat") ?
1701                     document.documentElement.clientWidth :
1702                     document.body.clientWidth;
1703         }
1704         return width;
1705     },
1706
1707     isAncestor : function(p, c) {
1708         p = Roo.getDom(p);
1709         c = Roo.getDom(c);
1710         if (!p || !c) {
1711             return false;
1712         }
1713
1714         if (p.contains && !Roo.isSafari) {
1715             return p.contains(c);
1716         } else if (p.compareDocumentPosition) {
1717             return !!(p.compareDocumentPosition(c) & 16);
1718         } else {
1719             var parent = c.parentNode;
1720             while (parent) {
1721                 if (parent == p) {
1722                     return true;
1723                 }
1724                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1725                     return false;
1726                 }
1727                 parent = parent.parentNode;
1728             }
1729             return false;
1730         }
1731     },
1732
1733     getRegion : function(el) {
1734         return Roo.lib.Region.getRegion(el);
1735     },
1736
1737     getY : function(el) {
1738         return this.getXY(el)[1];
1739     },
1740
1741     getX : function(el) {
1742         return this.getXY(el)[0];
1743     },
1744
1745     getXY : function(el) {
1746         var p, pe, b, scroll, bd = document.body;
1747         el = Roo.getDom(el);
1748         var fly = Roo.lib.AnimBase.fly;
1749         if (el.getBoundingClientRect) {
1750             b = el.getBoundingClientRect();
1751             scroll = fly(document).getScroll();
1752             return [b.left + scroll.left, b.top + scroll.top];
1753         }
1754         var x = 0, y = 0;
1755
1756         p = el;
1757
1758         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1759
1760         while (p) {
1761
1762             x += p.offsetLeft;
1763             y += p.offsetTop;
1764
1765             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1766                 hasAbsolute = true;
1767             }
1768
1769             if (Roo.isGecko) {
1770                 pe = fly(p);
1771
1772                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1773                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1774
1775
1776                 x += bl;
1777                 y += bt;
1778
1779
1780                 if (p != el && pe.getStyle('overflow') != 'visible') {
1781                     x += bl;
1782                     y += bt;
1783                 }
1784             }
1785             p = p.offsetParent;
1786         }
1787
1788         if (Roo.isSafari && hasAbsolute) {
1789             x -= bd.offsetLeft;
1790             y -= bd.offsetTop;
1791         }
1792
1793         if (Roo.isGecko && !hasAbsolute) {
1794             var dbd = fly(bd);
1795             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1796             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1797         }
1798
1799         p = el.parentNode;
1800         while (p && p != bd) {
1801             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1802                 x -= p.scrollLeft;
1803                 y -= p.scrollTop;
1804             }
1805             p = p.parentNode;
1806         }
1807         return [x, y];
1808     },
1809  
1810   
1811
1812
1813     setXY : function(el, xy) {
1814         el = Roo.fly(el, '_setXY');
1815         el.position();
1816         var pts = el.translatePoints(xy);
1817         if (xy[0] !== false) {
1818             el.dom.style.left = pts.left + "px";
1819         }
1820         if (xy[1] !== false) {
1821             el.dom.style.top = pts.top + "px";
1822         }
1823     },
1824
1825     setX : function(el, x) {
1826         this.setXY(el, [x, false]);
1827     },
1828
1829     setY : function(el, y) {
1830         this.setXY(el, [false, y]);
1831     }
1832 };
1833 /*
1834  * Portions of this file are based on pieces of Yahoo User Interface Library
1835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1836  * YUI licensed under the BSD License:
1837  * http://developer.yahoo.net/yui/license.txt
1838  * <script type="text/javascript">
1839  *
1840  */
1841
1842 Roo.lib.Event = function() {
1843     var loadComplete = false;
1844     var listeners = [];
1845     var unloadListeners = [];
1846     var retryCount = 0;
1847     var onAvailStack = [];
1848     var counter = 0;
1849     var lastError = null;
1850
1851     return {
1852         POLL_RETRYS: 200,
1853         POLL_INTERVAL: 20,
1854         EL: 0,
1855         TYPE: 1,
1856         FN: 2,
1857         WFN: 3,
1858         OBJ: 3,
1859         ADJ_SCOPE: 4,
1860         _interval: null,
1861
1862         startInterval: function() {
1863             if (!this._interval) {
1864                 var self = this;
1865                 var callback = function() {
1866                     self._tryPreloadAttach();
1867                 };
1868                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1869
1870             }
1871         },
1872
1873         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1874             onAvailStack.push({ id:         p_id,
1875                 fn:         p_fn,
1876                 obj:        p_obj,
1877                 override:   p_override,
1878                 checkReady: false    });
1879
1880             retryCount = this.POLL_RETRYS;
1881             this.startInterval();
1882         },
1883
1884
1885         addListener: function(el, eventName, fn) {
1886             el = Roo.getDom(el);
1887             if (!el || !fn) {
1888                 return false;
1889             }
1890
1891             if ("unload" == eventName) {
1892                 unloadListeners[unloadListeners.length] =
1893                 [el, eventName, fn];
1894                 return true;
1895             }
1896
1897             var wrappedFn = function(e) {
1898                 return fn(Roo.lib.Event.getEvent(e));
1899             };
1900
1901             var li = [el, eventName, fn, wrappedFn];
1902
1903             var index = listeners.length;
1904             listeners[index] = li;
1905
1906             this.doAdd(el, eventName, wrappedFn, false);
1907             return true;
1908
1909         },
1910
1911
1912         removeListener: function(el, eventName, fn) {
1913             var i, len;
1914
1915             el = Roo.getDom(el);
1916
1917             if(!fn) {
1918                 return this.purgeElement(el, false, eventName);
1919             }
1920
1921
1922             if ("unload" == eventName) {
1923
1924                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1925                     var li = unloadListeners[i];
1926                     if (li &&
1927                         li[0] == el &&
1928                         li[1] == eventName &&
1929                         li[2] == fn) {
1930                         unloadListeners.splice(i, 1);
1931                         return true;
1932                     }
1933                 }
1934
1935                 return false;
1936             }
1937
1938             var cacheItem = null;
1939
1940
1941             var index = arguments[3];
1942
1943             if ("undefined" == typeof index) {
1944                 index = this._getCacheIndex(el, eventName, fn);
1945             }
1946
1947             if (index >= 0) {
1948                 cacheItem = listeners[index];
1949             }
1950
1951             if (!el || !cacheItem) {
1952                 return false;
1953             }
1954
1955             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1956
1957             delete listeners[index][this.WFN];
1958             delete listeners[index][this.FN];
1959             listeners.splice(index, 1);
1960
1961             return true;
1962
1963         },
1964
1965
1966         getTarget: function(ev, resolveTextNode) {
1967             ev = ev.browserEvent || ev;
1968             var t = ev.target || ev.srcElement;
1969             return this.resolveTextNode(t);
1970         },
1971
1972
1973         resolveTextNode: function(node) {
1974             if (Roo.isSafari && node && 3 == node.nodeType) {
1975                 return node.parentNode;
1976             } else {
1977                 return node;
1978             }
1979         },
1980
1981
1982         getPageX: function(ev) {
1983             ev = ev.browserEvent || ev;
1984             var x = ev.pageX;
1985             if (!x && 0 !== x) {
1986                 x = ev.clientX || 0;
1987
1988                 if (Roo.isIE) {
1989                     x += this.getScroll()[1];
1990                 }
1991             }
1992
1993             return x;
1994         },
1995
1996
1997         getPageY: function(ev) {
1998             ev = ev.browserEvent || ev;
1999             var y = ev.pageY;
2000             if (!y && 0 !== y) {
2001                 y = ev.clientY || 0;
2002
2003                 if (Roo.isIE) {
2004                     y += this.getScroll()[0];
2005                 }
2006             }
2007
2008
2009             return y;
2010         },
2011
2012
2013         getXY: function(ev) {
2014             ev = ev.browserEvent || ev;
2015             return [this.getPageX(ev), this.getPageY(ev)];
2016         },
2017
2018
2019         getRelatedTarget: function(ev) {
2020             ev = ev.browserEvent || ev;
2021             var t = ev.relatedTarget;
2022             if (!t) {
2023                 if (ev.type == "mouseout") {
2024                     t = ev.toElement;
2025                 } else if (ev.type == "mouseover") {
2026                     t = ev.fromElement;
2027                 }
2028             }
2029
2030             return this.resolveTextNode(t);
2031         },
2032
2033
2034         getTime: function(ev) {
2035             ev = ev.browserEvent || ev;
2036             if (!ev.time) {
2037                 var t = new Date().getTime();
2038                 try {
2039                     ev.time = t;
2040                 } catch(ex) {
2041                     this.lastError = ex;
2042                     return t;
2043                 }
2044             }
2045
2046             return ev.time;
2047         },
2048
2049
2050         stopEvent: function(ev) {
2051             this.stopPropagation(ev);
2052             this.preventDefault(ev);
2053         },
2054
2055
2056         stopPropagation: function(ev) {
2057             ev = ev.browserEvent || ev;
2058             if (ev.stopPropagation) {
2059                 ev.stopPropagation();
2060             } else {
2061                 ev.cancelBubble = true;
2062             }
2063         },
2064
2065
2066         preventDefault: function(ev) {
2067             ev = ev.browserEvent || ev;
2068             if(ev.preventDefault) {
2069                 ev.preventDefault();
2070             } else {
2071                 ev.returnValue = false;
2072             }
2073         },
2074
2075
2076         getEvent: function(e) {
2077             var ev = e || window.event;
2078             if (!ev) {
2079                 var c = this.getEvent.caller;
2080                 while (c) {
2081                     ev = c.arguments[0];
2082                     if (ev && Event == ev.constructor) {
2083                         break;
2084                     }
2085                     c = c.caller;
2086                 }
2087             }
2088             return ev;
2089         },
2090
2091
2092         getCharCode: function(ev) {
2093             ev = ev.browserEvent || ev;
2094             return ev.charCode || ev.keyCode || 0;
2095         },
2096
2097
2098         _getCacheIndex: function(el, eventName, fn) {
2099             for (var i = 0,len = listeners.length; i < len; ++i) {
2100                 var li = listeners[i];
2101                 if (li &&
2102                     li[this.FN] == fn &&
2103                     li[this.EL] == el &&
2104                     li[this.TYPE] == eventName) {
2105                     return i;
2106                 }
2107             }
2108
2109             return -1;
2110         },
2111
2112
2113         elCache: {},
2114
2115
2116         getEl: function(id) {
2117             return document.getElementById(id);
2118         },
2119
2120
2121         clearCache: function() {
2122         },
2123
2124
2125         _load: function(e) {
2126             loadComplete = true;
2127             var EU = Roo.lib.Event;
2128
2129
2130             if (Roo.isIE) {
2131                 EU.doRemove(window, "load", EU._load);
2132             }
2133         },
2134
2135
2136         _tryPreloadAttach: function() {
2137
2138             if (this.locked) {
2139                 return false;
2140             }
2141
2142             this.locked = true;
2143
2144
2145             var tryAgain = !loadComplete;
2146             if (!tryAgain) {
2147                 tryAgain = (retryCount > 0);
2148             }
2149
2150
2151             var notAvail = [];
2152             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2153                 var item = onAvailStack[i];
2154                 if (item) {
2155                     var el = this.getEl(item.id);
2156
2157                     if (el) {
2158                         if (!item.checkReady ||
2159                             loadComplete ||
2160                             el.nextSibling ||
2161                             (document && document.body)) {
2162
2163                             var scope = el;
2164                             if (item.override) {
2165                                 if (item.override === true) {
2166                                     scope = item.obj;
2167                                 } else {
2168                                     scope = item.override;
2169                                 }
2170                             }
2171                             item.fn.call(scope, item.obj);
2172                             onAvailStack[i] = null;
2173                         }
2174                     } else {
2175                         notAvail.push(item);
2176                     }
2177                 }
2178             }
2179
2180             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2181
2182             if (tryAgain) {
2183
2184                 this.startInterval();
2185             } else {
2186                 clearInterval(this._interval);
2187                 this._interval = null;
2188             }
2189
2190             this.locked = false;
2191
2192             return true;
2193
2194         },
2195
2196
2197         purgeElement: function(el, recurse, eventName) {
2198             var elListeners = this.getListeners(el, eventName);
2199             if (elListeners) {
2200                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2201                     var l = elListeners[i];
2202                     this.removeListener(el, l.type, l.fn);
2203                 }
2204             }
2205
2206             if (recurse && el && el.childNodes) {
2207                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2208                     this.purgeElement(el.childNodes[i], recurse, eventName);
2209                 }
2210             }
2211         },
2212
2213
2214         getListeners: function(el, eventName) {
2215             var results = [], searchLists;
2216             if (!eventName) {
2217                 searchLists = [listeners, unloadListeners];
2218             } else if (eventName == "unload") {
2219                 searchLists = [unloadListeners];
2220             } else {
2221                 searchLists = [listeners];
2222             }
2223
2224             for (var j = 0; j < searchLists.length; ++j) {
2225                 var searchList = searchLists[j];
2226                 if (searchList && searchList.length > 0) {
2227                     for (var i = 0,len = searchList.length; i < len; ++i) {
2228                         var l = searchList[i];
2229                         if (l && l[this.EL] === el &&
2230                             (!eventName || eventName === l[this.TYPE])) {
2231                             results.push({
2232                                 type:   l[this.TYPE],
2233                                 fn:     l[this.FN],
2234                                 obj:    l[this.OBJ],
2235                                 adjust: l[this.ADJ_SCOPE],
2236                                 index:  i
2237                             });
2238                         }
2239                     }
2240                 }
2241             }
2242
2243             return (results.length) ? results : null;
2244         },
2245
2246
2247         _unload: function(e) {
2248
2249             var EU = Roo.lib.Event, i, j, l, len, index;
2250
2251             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2252                 l = unloadListeners[i];
2253                 if (l) {
2254                     var scope = window;
2255                     if (l[EU.ADJ_SCOPE]) {
2256                         if (l[EU.ADJ_SCOPE] === true) {
2257                             scope = l[EU.OBJ];
2258                         } else {
2259                             scope = l[EU.ADJ_SCOPE];
2260                         }
2261                     }
2262                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2263                     unloadListeners[i] = null;
2264                     l = null;
2265                     scope = null;
2266                 }
2267             }
2268
2269             unloadListeners = null;
2270
2271             if (listeners && listeners.length > 0) {
2272                 j = listeners.length;
2273                 while (j) {
2274                     index = j - 1;
2275                     l = listeners[index];
2276                     if (l) {
2277                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2278                                 l[EU.FN], index);
2279                     }
2280                     j = j - 1;
2281                 }
2282                 l = null;
2283
2284                 EU.clearCache();
2285             }
2286
2287             EU.doRemove(window, "unload", EU._unload);
2288
2289         },
2290
2291
2292         getScroll: function() {
2293             var dd = document.documentElement, db = document.body;
2294             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2295                 return [dd.scrollTop, dd.scrollLeft];
2296             } else if (db) {
2297                 return [db.scrollTop, db.scrollLeft];
2298             } else {
2299                 return [0, 0];
2300             }
2301         },
2302
2303
2304         doAdd: function () {
2305             if (window.addEventListener) {
2306                 return function(el, eventName, fn, capture) {
2307                     el.addEventListener(eventName, fn, (capture));
2308                 };
2309             } else if (window.attachEvent) {
2310                 return function(el, eventName, fn, capture) {
2311                     el.attachEvent("on" + eventName, fn);
2312                 };
2313             } else {
2314                 return function() {
2315                 };
2316             }
2317         }(),
2318
2319
2320         doRemove: function() {
2321             if (window.removeEventListener) {
2322                 return function (el, eventName, fn, capture) {
2323                     el.removeEventListener(eventName, fn, (capture));
2324                 };
2325             } else if (window.detachEvent) {
2326                 return function (el, eventName, fn) {
2327                     el.detachEvent("on" + eventName, fn);
2328                 };
2329             } else {
2330                 return function() {
2331                 };
2332             }
2333         }()
2334     };
2335     
2336 }();
2337 (function() {     
2338    
2339     var E = Roo.lib.Event;
2340     E.on = E.addListener;
2341     E.un = E.removeListener;
2342
2343     if (document && document.body) {
2344         E._load();
2345     } else {
2346         E.doAdd(window, "load", E._load);
2347     }
2348     E.doAdd(window, "unload", E._unload);
2349     E._tryPreloadAttach();
2350 })();
2351
2352 /*
2353  * Portions of this file are based on pieces of Yahoo User Interface Library
2354  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2355  * YUI licensed under the BSD License:
2356  * http://developer.yahoo.net/yui/license.txt
2357  * <script type="text/javascript">
2358  *
2359  */
2360
2361 (function() {
2362     
2363     Roo.lib.Ajax = {
2364         request : function(method, uri, cb, data, options) {
2365             if(options){
2366                 var hs = options.headers;
2367                 if(hs){
2368                     for(var h in hs){
2369                         if(hs.hasOwnProperty(h)){
2370                             this.initHeader(h, hs[h], false);
2371                         }
2372                     }
2373                 }
2374                 if(options.xmlData){
2375                     this.initHeader('Content-Type', 'text/xml', false);
2376                     method = 'POST';
2377                     data = options.xmlData;
2378                 }
2379             }
2380
2381             return this.asyncRequest(method, uri, cb, data);
2382         },
2383
2384         serializeForm : function(form) {
2385             if(typeof form == 'string') {
2386                 form = (document.getElementById(form) || document.forms[form]);
2387             }
2388
2389             var el, name, val, disabled, data = '', hasSubmit = false;
2390             for (var i = 0; i < form.elements.length; i++) {
2391                 el = form.elements[i];
2392                 disabled = form.elements[i].disabled;
2393                 name = form.elements[i].name;
2394                 val = form.elements[i].value;
2395
2396                 if (!disabled && name){
2397                     switch (el.type)
2398                             {
2399                         case 'select-one':
2400                         case 'select-multiple':
2401                             for (var j = 0; j < el.options.length; j++) {
2402                                 if (el.options[j].selected) {
2403                                     if (Roo.isIE) {
2404                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2405                                     }
2406                                     else {
2407                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2408                                     }
2409                                 }
2410                             }
2411                             break;
2412                         case 'radio':
2413                         case 'checkbox':
2414                             if (el.checked) {
2415                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2416                             }
2417                             break;
2418                         case 'file':
2419
2420                         case undefined:
2421
2422                         case 'reset':
2423
2424                         case 'button':
2425
2426                             break;
2427                         case 'submit':
2428                             if(hasSubmit == false) {
2429                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2430                                 hasSubmit = true;
2431                             }
2432                             break;
2433                         default:
2434                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2435                             break;
2436                     }
2437                 }
2438             }
2439             data = data.substr(0, data.length - 1);
2440             return data;
2441         },
2442
2443         headers:{},
2444
2445         hasHeaders:false,
2446
2447         useDefaultHeader:true,
2448
2449         defaultPostHeader:'application/x-www-form-urlencoded',
2450
2451         useDefaultXhrHeader:true,
2452
2453         defaultXhrHeader:'XMLHttpRequest',
2454
2455         hasDefaultHeaders:true,
2456
2457         defaultHeaders:{},
2458
2459         poll:{},
2460
2461         timeout:{},
2462
2463         pollInterval:50,
2464
2465         transactionId:0,
2466
2467         setProgId:function(id)
2468         {
2469             this.activeX.unshift(id);
2470         },
2471
2472         setDefaultPostHeader:function(b)
2473         {
2474             this.useDefaultHeader = b;
2475         },
2476
2477         setDefaultXhrHeader:function(b)
2478         {
2479             this.useDefaultXhrHeader = b;
2480         },
2481
2482         setPollingInterval:function(i)
2483         {
2484             if (typeof i == 'number' && isFinite(i)) {
2485                 this.pollInterval = i;
2486             }
2487         },
2488
2489         createXhrObject:function(transactionId)
2490         {
2491             var obj,http;
2492             try
2493             {
2494
2495                 http = new XMLHttpRequest();
2496
2497                 obj = { conn:http, tId:transactionId };
2498             }
2499             catch(e)
2500             {
2501                 for (var i = 0; i < this.activeX.length; ++i) {
2502                     try
2503                     {
2504
2505                         http = new ActiveXObject(this.activeX[i]);
2506
2507                         obj = { conn:http, tId:transactionId };
2508                         break;
2509                     }
2510                     catch(e) {
2511                     }
2512                 }
2513             }
2514             finally
2515             {
2516                 return obj;
2517             }
2518         },
2519
2520         getConnectionObject:function()
2521         {
2522             var o;
2523             var tId = this.transactionId;
2524
2525             try
2526             {
2527                 o = this.createXhrObject(tId);
2528                 if (o) {
2529                     this.transactionId++;
2530                 }
2531             }
2532             catch(e) {
2533             }
2534             finally
2535             {
2536                 return o;
2537             }
2538         },
2539
2540         asyncRequest:function(method, uri, callback, postData)
2541         {
2542             var o = this.getConnectionObject();
2543
2544             if (!o) {
2545                 return null;
2546             }
2547             else {
2548                 o.conn.open(method, uri, true);
2549
2550                 if (this.useDefaultXhrHeader) {
2551                     if (!this.defaultHeaders['X-Requested-With']) {
2552                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2553                     }
2554                 }
2555
2556                 if(postData && this.useDefaultHeader){
2557                     this.initHeader('Content-Type', this.defaultPostHeader);
2558                 }
2559
2560                  if (this.hasDefaultHeaders || this.hasHeaders) {
2561                     this.setHeader(o);
2562                 }
2563
2564                 this.handleReadyState(o, callback);
2565                 o.conn.send(postData || null);
2566
2567                 return o;
2568             }
2569         },
2570
2571         handleReadyState:function(o, callback)
2572         {
2573             var oConn = this;
2574
2575             if (callback && callback.timeout) {
2576                 this.timeout[o.tId] = window.setTimeout(function() {
2577                     oConn.abort(o, callback, true);
2578                 }, callback.timeout);
2579             }
2580
2581             this.poll[o.tId] = window.setInterval(
2582                     function() {
2583                         if (o.conn && o.conn.readyState == 4) {
2584                             window.clearInterval(oConn.poll[o.tId]);
2585                             delete oConn.poll[o.tId];
2586
2587                             if(callback && callback.timeout) {
2588                                 window.clearTimeout(oConn.timeout[o.tId]);
2589                                 delete oConn.timeout[o.tId];
2590                             }
2591
2592                             oConn.handleTransactionResponse(o, callback);
2593                         }
2594                     }
2595                     , this.pollInterval);
2596         },
2597
2598         handleTransactionResponse:function(o, callback, isAbort)
2599         {
2600
2601             if (!callback) {
2602                 this.releaseObject(o);
2603                 return;
2604             }
2605
2606             var httpStatus, responseObject;
2607
2608             try
2609             {
2610                 if (o.conn.status !== undefined && o.conn.status != 0) {
2611                     httpStatus = o.conn.status;
2612                 }
2613                 else {
2614                     httpStatus = 13030;
2615                 }
2616             }
2617             catch(e) {
2618
2619
2620                 httpStatus = 13030;
2621             }
2622
2623             if (httpStatus >= 200 && httpStatus < 300) {
2624                 responseObject = this.createResponseObject(o, callback.argument);
2625                 if (callback.success) {
2626                     if (!callback.scope) {
2627                         callback.success(responseObject);
2628                     }
2629                     else {
2630
2631
2632                         callback.success.apply(callback.scope, [responseObject]);
2633                     }
2634                 }
2635             }
2636             else {
2637                 switch (httpStatus) {
2638
2639                     case 12002:
2640                     case 12029:
2641                     case 12030:
2642                     case 12031:
2643                     case 12152:
2644                     case 13030:
2645                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2646                         if (callback.failure) {
2647                             if (!callback.scope) {
2648                                 callback.failure(responseObject);
2649                             }
2650                             else {
2651                                 callback.failure.apply(callback.scope, [responseObject]);
2652                             }
2653                         }
2654                         break;
2655                     default:
2656                         responseObject = this.createResponseObject(o, callback.argument);
2657                         if (callback.failure) {
2658                             if (!callback.scope) {
2659                                 callback.failure(responseObject);
2660                             }
2661                             else {
2662                                 callback.failure.apply(callback.scope, [responseObject]);
2663                             }
2664                         }
2665                 }
2666             }
2667
2668             this.releaseObject(o);
2669             responseObject = null;
2670         },
2671
2672         createResponseObject:function(o, callbackArg)
2673         {
2674             var obj = {};
2675             var headerObj = {};
2676
2677             try
2678             {
2679                 var headerStr = o.conn.getAllResponseHeaders();
2680                 var header = headerStr.split('\n');
2681                 for (var i = 0; i < header.length; i++) {
2682                     var delimitPos = header[i].indexOf(':');
2683                     if (delimitPos != -1) {
2684                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2685                     }
2686                 }
2687             }
2688             catch(e) {
2689             }
2690
2691             obj.tId = o.tId;
2692             obj.status = o.conn.status;
2693             obj.statusText = o.conn.statusText;
2694             obj.getResponseHeader = headerObj;
2695             obj.getAllResponseHeaders = headerStr;
2696             obj.responseText = o.conn.responseText;
2697             obj.responseXML = o.conn.responseXML;
2698
2699             if (typeof callbackArg !== undefined) {
2700                 obj.argument = callbackArg;
2701             }
2702
2703             return obj;
2704         },
2705
2706         createExceptionObject:function(tId, callbackArg, isAbort)
2707         {
2708             var COMM_CODE = 0;
2709             var COMM_ERROR = 'communication failure';
2710             var ABORT_CODE = -1;
2711             var ABORT_ERROR = 'transaction aborted';
2712
2713             var obj = {};
2714
2715             obj.tId = tId;
2716             if (isAbort) {
2717                 obj.status = ABORT_CODE;
2718                 obj.statusText = ABORT_ERROR;
2719             }
2720             else {
2721                 obj.status = COMM_CODE;
2722                 obj.statusText = COMM_ERROR;
2723             }
2724
2725             if (callbackArg) {
2726                 obj.argument = callbackArg;
2727             }
2728
2729             return obj;
2730         },
2731
2732         initHeader:function(label, value, isDefault)
2733         {
2734             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2735
2736             if (headerObj[label] === undefined) {
2737                 headerObj[label] = value;
2738             }
2739             else {
2740
2741
2742                 headerObj[label] = value + "," + headerObj[label];
2743             }
2744
2745             if (isDefault) {
2746                 this.hasDefaultHeaders = true;
2747             }
2748             else {
2749                 this.hasHeaders = true;
2750             }
2751         },
2752
2753
2754         setHeader:function(o)
2755         {
2756             if (this.hasDefaultHeaders) {
2757                 for (var prop in this.defaultHeaders) {
2758                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2759                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2760                     }
2761                 }
2762             }
2763
2764             if (this.hasHeaders) {
2765                 for (var prop in this.headers) {
2766                     if (this.headers.hasOwnProperty(prop)) {
2767                         o.conn.setRequestHeader(prop, this.headers[prop]);
2768                     }
2769                 }
2770                 this.headers = {};
2771                 this.hasHeaders = false;
2772             }
2773         },
2774
2775         resetDefaultHeaders:function() {
2776             delete this.defaultHeaders;
2777             this.defaultHeaders = {};
2778             this.hasDefaultHeaders = false;
2779         },
2780
2781         abort:function(o, callback, isTimeout)
2782         {
2783             if(this.isCallInProgress(o)) {
2784                 o.conn.abort();
2785                 window.clearInterval(this.poll[o.tId]);
2786                 delete this.poll[o.tId];
2787                 if (isTimeout) {
2788                     delete this.timeout[o.tId];
2789                 }
2790
2791                 this.handleTransactionResponse(o, callback, true);
2792
2793                 return true;
2794             }
2795             else {
2796                 return false;
2797             }
2798         },
2799
2800
2801         isCallInProgress:function(o)
2802         {
2803             if (o && o.conn) {
2804                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2805             }
2806             else {
2807
2808                 return false;
2809             }
2810         },
2811
2812
2813         releaseObject:function(o)
2814         {
2815
2816             o.conn = null;
2817
2818             o = null;
2819         },
2820
2821         activeX:[
2822         'MSXML2.XMLHTTP.3.0',
2823         'MSXML2.XMLHTTP',
2824         'Microsoft.XMLHTTP'
2825         ]
2826
2827
2828     };
2829 })();/*
2830  * Portions of this file are based on pieces of Yahoo User Interface Library
2831  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2832  * YUI licensed under the BSD License:
2833  * http://developer.yahoo.net/yui/license.txt
2834  * <script type="text/javascript">
2835  *
2836  */
2837
2838 Roo.lib.Region = function(t, r, b, l) {
2839     this.top = t;
2840     this[1] = t;
2841     this.right = r;
2842     this.bottom = b;
2843     this.left = l;
2844     this[0] = l;
2845 };
2846
2847
2848 Roo.lib.Region.prototype = {
2849     contains : function(region) {
2850         return ( region.left >= this.left &&
2851                  region.right <= this.right &&
2852                  region.top >= this.top &&
2853                  region.bottom <= this.bottom    );
2854
2855     },
2856
2857     getArea : function() {
2858         return ( (this.bottom - this.top) * (this.right - this.left) );
2859     },
2860
2861     intersect : function(region) {
2862         var t = Math.max(this.top, region.top);
2863         var r = Math.min(this.right, region.right);
2864         var b = Math.min(this.bottom, region.bottom);
2865         var l = Math.max(this.left, region.left);
2866
2867         if (b >= t && r >= l) {
2868             return new Roo.lib.Region(t, r, b, l);
2869         } else {
2870             return null;
2871         }
2872     },
2873     union : function(region) {
2874         var t = Math.min(this.top, region.top);
2875         var r = Math.max(this.right, region.right);
2876         var b = Math.max(this.bottom, region.bottom);
2877         var l = Math.min(this.left, region.left);
2878
2879         return new Roo.lib.Region(t, r, b, l);
2880     },
2881
2882     adjust : function(t, l, b, r) {
2883         this.top += t;
2884         this.left += l;
2885         this.right += r;
2886         this.bottom += b;
2887         return this;
2888     }
2889 };
2890
2891 Roo.lib.Region.getRegion = function(el) {
2892     var p = Roo.lib.Dom.getXY(el);
2893
2894     var t = p[1];
2895     var r = p[0] + el.offsetWidth;
2896     var b = p[1] + el.offsetHeight;
2897     var l = p[0];
2898
2899     return new Roo.lib.Region(t, r, b, l);
2900 };
2901 /*
2902  * Portions of this file are based on pieces of Yahoo User Interface Library
2903  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2904  * YUI licensed under the BSD License:
2905  * http://developer.yahoo.net/yui/license.txt
2906  * <script type="text/javascript">
2907  *
2908  */
2909 //@@dep Roo.lib.Region
2910
2911
2912 Roo.lib.Point = function(x, y) {
2913     if (x instanceof Array) {
2914         y = x[1];
2915         x = x[0];
2916     }
2917     this.x = this.right = this.left = this[0] = x;
2918     this.y = this.top = this.bottom = this[1] = y;
2919 };
2920
2921 Roo.lib.Point.prototype = new Roo.lib.Region();
2922 /*
2923  * Portions of this file are based on pieces of Yahoo User Interface Library
2924  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2925  * YUI licensed under the BSD License:
2926  * http://developer.yahoo.net/yui/license.txt
2927  * <script type="text/javascript">
2928  *
2929  */
2930  
2931 (function() {   
2932
2933     Roo.lib.Anim = {
2934         scroll : function(el, args, duration, easing, cb, scope) {
2935             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2936         },
2937
2938         motion : function(el, args, duration, easing, cb, scope) {
2939             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2940         },
2941
2942         color : function(el, args, duration, easing, cb, scope) {
2943             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2944         },
2945
2946         run : function(el, args, duration, easing, cb, scope, type) {
2947             type = type || Roo.lib.AnimBase;
2948             if (typeof easing == "string") {
2949                 easing = Roo.lib.Easing[easing];
2950             }
2951             var anim = new type(el, args, duration, easing);
2952             anim.animateX(function() {
2953                 Roo.callback(cb, scope);
2954             });
2955             return anim;
2956         }
2957     };
2958 })();/*
2959  * Portions of this file are based on pieces of Yahoo User Interface Library
2960  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2961  * YUI licensed under the BSD License:
2962  * http://developer.yahoo.net/yui/license.txt
2963  * <script type="text/javascript">
2964  *
2965  */
2966
2967 (function() {    
2968     var libFlyweight;
2969     
2970     function fly(el) {
2971         if (!libFlyweight) {
2972             libFlyweight = new Roo.Element.Flyweight();
2973         }
2974         libFlyweight.dom = el;
2975         return libFlyweight;
2976     }
2977
2978     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2979     
2980    
2981     
2982     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2983         if (el) {
2984             this.init(el, attributes, duration, method);
2985         }
2986     };
2987
2988     Roo.lib.AnimBase.fly = fly;
2989     
2990     
2991     
2992     Roo.lib.AnimBase.prototype = {
2993
2994         toString: function() {
2995             var el = this.getEl();
2996             var id = el.id || el.tagName;
2997             return ("Anim " + id);
2998         },
2999
3000         patterns: {
3001             noNegatives:        /width|height|opacity|padding/i,
3002             offsetAttribute:  /^((width|height)|(top|left))$/,
3003             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3004             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3005         },
3006
3007
3008         doMethod: function(attr, start, end) {
3009             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3010         },
3011
3012
3013         setAttribute: function(attr, val, unit) {
3014             if (this.patterns.noNegatives.test(attr)) {
3015                 val = (val > 0) ? val : 0;
3016             }
3017
3018             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3019         },
3020
3021
3022         getAttribute: function(attr) {
3023             var el = this.getEl();
3024             var val = fly(el).getStyle(attr);
3025
3026             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3027                 return parseFloat(val);
3028             }
3029
3030             var a = this.patterns.offsetAttribute.exec(attr) || [];
3031             var pos = !!( a[3] );
3032             var box = !!( a[2] );
3033
3034
3035             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3036                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3037             } else {
3038                 val = 0;
3039             }
3040
3041             return val;
3042         },
3043
3044
3045         getDefaultUnit: function(attr) {
3046             if (this.patterns.defaultUnit.test(attr)) {
3047                 return 'px';
3048             }
3049
3050             return '';
3051         },
3052
3053         animateX : function(callback, scope) {
3054             var f = function() {
3055                 this.onComplete.removeListener(f);
3056                 if (typeof callback == "function") {
3057                     callback.call(scope || this, this);
3058                 }
3059             };
3060             this.onComplete.addListener(f, this);
3061             this.animate();
3062         },
3063
3064
3065         setRuntimeAttribute: function(attr) {
3066             var start;
3067             var end;
3068             var attributes = this.attributes;
3069
3070             this.runtimeAttributes[attr] = {};
3071
3072             var isset = function(prop) {
3073                 return (typeof prop !== 'undefined');
3074             };
3075
3076             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3077                 return false;
3078             }
3079
3080             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3081
3082
3083             if (isset(attributes[attr]['to'])) {
3084                 end = attributes[attr]['to'];
3085             } else if (isset(attributes[attr]['by'])) {
3086                 if (start.constructor == Array) {
3087                     end = [];
3088                     for (var i = 0, len = start.length; i < len; ++i) {
3089                         end[i] = start[i] + attributes[attr]['by'][i];
3090                     }
3091                 } else {
3092                     end = start + attributes[attr]['by'];
3093                 }
3094             }
3095
3096             this.runtimeAttributes[attr].start = start;
3097             this.runtimeAttributes[attr].end = end;
3098
3099
3100             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3101         },
3102
3103
3104         init: function(el, attributes, duration, method) {
3105
3106             var isAnimated = false;
3107
3108
3109             var startTime = null;
3110
3111
3112             var actualFrames = 0;
3113
3114
3115             el = Roo.getDom(el);
3116
3117
3118             this.attributes = attributes || {};
3119
3120
3121             this.duration = duration || 1;
3122
3123
3124             this.method = method || Roo.lib.Easing.easeNone;
3125
3126
3127             this.useSeconds = true;
3128
3129
3130             this.currentFrame = 0;
3131
3132
3133             this.totalFrames = Roo.lib.AnimMgr.fps;
3134
3135
3136             this.getEl = function() {
3137                 return el;
3138             };
3139
3140
3141             this.isAnimated = function() {
3142                 return isAnimated;
3143             };
3144
3145
3146             this.getStartTime = function() {
3147                 return startTime;
3148             };
3149
3150             this.runtimeAttributes = {};
3151
3152
3153             this.animate = function() {
3154                 if (this.isAnimated()) {
3155                     return false;
3156                 }
3157
3158                 this.currentFrame = 0;
3159
3160                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3161
3162                 Roo.lib.AnimMgr.registerElement(this);
3163             };
3164
3165
3166             this.stop = function(finish) {
3167                 if (finish) {
3168                     this.currentFrame = this.totalFrames;
3169                     this._onTween.fire();
3170                 }
3171                 Roo.lib.AnimMgr.stop(this);
3172             };
3173
3174             var onStart = function() {
3175                 this.onStart.fire();
3176
3177                 this.runtimeAttributes = {};
3178                 for (var attr in this.attributes) {
3179                     this.setRuntimeAttribute(attr);
3180                 }
3181
3182                 isAnimated = true;
3183                 actualFrames = 0;
3184                 startTime = new Date();
3185             };
3186
3187
3188             var onTween = function() {
3189                 var data = {
3190                     duration: new Date() - this.getStartTime(),
3191                     currentFrame: this.currentFrame
3192                 };
3193
3194                 data.toString = function() {
3195                     return (
3196                             'duration: ' + data.duration +
3197                             ', currentFrame: ' + data.currentFrame
3198                             );
3199                 };
3200
3201                 this.onTween.fire(data);
3202
3203                 var runtimeAttributes = this.runtimeAttributes;
3204
3205                 for (var attr in runtimeAttributes) {
3206                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3207                 }
3208
3209                 actualFrames += 1;
3210             };
3211
3212             var onComplete = function() {
3213                 var actual_duration = (new Date() - startTime) / 1000 ;
3214
3215                 var data = {
3216                     duration: actual_duration,
3217                     frames: actualFrames,
3218                     fps: actualFrames / actual_duration
3219                 };
3220
3221                 data.toString = function() {
3222                     return (
3223                             'duration: ' + data.duration +
3224                             ', frames: ' + data.frames +
3225                             ', fps: ' + data.fps
3226                             );
3227                 };
3228
3229                 isAnimated = false;
3230                 actualFrames = 0;
3231                 this.onComplete.fire(data);
3232             };
3233
3234
3235             this._onStart = new Roo.util.Event(this);
3236             this.onStart = new Roo.util.Event(this);
3237             this.onTween = new Roo.util.Event(this);
3238             this._onTween = new Roo.util.Event(this);
3239             this.onComplete = new Roo.util.Event(this);
3240             this._onComplete = new Roo.util.Event(this);
3241             this._onStart.addListener(onStart);
3242             this._onTween.addListener(onTween);
3243             this._onComplete.addListener(onComplete);
3244         }
3245     };
3246 })();
3247 /*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 Roo.lib.AnimMgr = new function() {
3257
3258         var thread = null;
3259
3260
3261         var queue = [];
3262
3263
3264         var tweenCount = 0;
3265
3266
3267         this.fps = 1000;
3268
3269
3270         this.delay = 1;
3271
3272
3273         this.registerElement = function(tween) {
3274             queue[queue.length] = tween;
3275             tweenCount += 1;
3276             tween._onStart.fire();
3277             this.start();
3278         };
3279
3280
3281         this.unRegister = function(tween, index) {
3282             tween._onComplete.fire();
3283             index = index || getIndex(tween);
3284             if (index != -1) {
3285                 queue.splice(index, 1);
3286             }
3287
3288             tweenCount -= 1;
3289             if (tweenCount <= 0) {
3290                 this.stop();
3291             }
3292         };
3293
3294
3295         this.start = function() {
3296             if (thread === null) {
3297                 thread = setInterval(this.run, this.delay);
3298             }
3299         };
3300
3301
3302         this.stop = function(tween) {
3303             if (!tween) {
3304                 clearInterval(thread);
3305
3306                 for (var i = 0, len = queue.length; i < len; ++i) {
3307                     if (queue[0].isAnimated()) {
3308                         this.unRegister(queue[0], 0);
3309                     }
3310                 }
3311
3312                 queue = [];
3313                 thread = null;
3314                 tweenCount = 0;
3315             }
3316             else {
3317                 this.unRegister(tween);
3318             }
3319         };
3320
3321
3322         this.run = function() {
3323             for (var i = 0, len = queue.length; i < len; ++i) {
3324                 var tween = queue[i];
3325                 if (!tween || !tween.isAnimated()) {
3326                     continue;
3327                 }
3328
3329                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3330                 {
3331                     tween.currentFrame += 1;
3332
3333                     if (tween.useSeconds) {
3334                         correctFrame(tween);
3335                     }
3336                     tween._onTween.fire();
3337                 }
3338                 else {
3339                     Roo.lib.AnimMgr.stop(tween, i);
3340                 }
3341             }
3342         };
3343
3344         var getIndex = function(anim) {
3345             for (var i = 0, len = queue.length; i < len; ++i) {
3346                 if (queue[i] == anim) {
3347                     return i;
3348                 }
3349             }
3350             return -1;
3351         };
3352
3353
3354         var correctFrame = function(tween) {
3355             var frames = tween.totalFrames;
3356             var frame = tween.currentFrame;
3357             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3358             var elapsed = (new Date() - tween.getStartTime());
3359             var tweak = 0;
3360
3361             if (elapsed < tween.duration * 1000) {
3362                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3363             } else {
3364                 tweak = frames - (frame + 1);
3365             }
3366             if (tweak > 0 && isFinite(tweak)) {
3367                 if (tween.currentFrame + tweak >= frames) {
3368                     tweak = frames - (frame + 1);
3369                 }
3370
3371                 tween.currentFrame += tweak;
3372             }
3373         };
3374     };/*
3375  * Portions of this file are based on pieces of Yahoo User Interface Library
3376  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3377  * YUI licensed under the BSD License:
3378  * http://developer.yahoo.net/yui/license.txt
3379  * <script type="text/javascript">
3380  *
3381  */
3382 Roo.lib.Bezier = new function() {
3383
3384         this.getPosition = function(points, t) {
3385             var n = points.length;
3386             var tmp = [];
3387
3388             for (var i = 0; i < n; ++i) {
3389                 tmp[i] = [points[i][0], points[i][1]];
3390             }
3391
3392             for (var j = 1; j < n; ++j) {
3393                 for (i = 0; i < n - j; ++i) {
3394                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3395                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3396                 }
3397             }
3398
3399             return [ tmp[0][0], tmp[0][1] ];
3400
3401         };
3402     };/*
3403  * Portions of this file are based on pieces of Yahoo User Interface Library
3404  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3405  * YUI licensed under the BSD License:
3406  * http://developer.yahoo.net/yui/license.txt
3407  * <script type="text/javascript">
3408  *
3409  */
3410 (function() {
3411
3412     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3413         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3414     };
3415
3416     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3417
3418     var fly = Roo.lib.AnimBase.fly;
3419     var Y = Roo.lib;
3420     var superclass = Y.ColorAnim.superclass;
3421     var proto = Y.ColorAnim.prototype;
3422
3423     proto.toString = function() {
3424         var el = this.getEl();
3425         var id = el.id || el.tagName;
3426         return ("ColorAnim " + id);
3427     };
3428
3429     proto.patterns.color = /color$/i;
3430     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3431     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3432     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3433     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3434
3435
3436     proto.parseColor = function(s) {
3437         if (s.length == 3) {
3438             return s;
3439         }
3440
3441         var c = this.patterns.hex.exec(s);
3442         if (c && c.length == 4) {
3443             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3444         }
3445
3446         c = this.patterns.rgb.exec(s);
3447         if (c && c.length == 4) {
3448             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3449         }
3450
3451         c = this.patterns.hex3.exec(s);
3452         if (c && c.length == 4) {
3453             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3454         }
3455
3456         return null;
3457     };
3458     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3459     proto.getAttribute = function(attr) {
3460         var el = this.getEl();
3461         if (this.patterns.color.test(attr)) {
3462             var val = fly(el).getStyle(attr);
3463
3464             if (this.patterns.transparent.test(val)) {
3465                 var parent = el.parentNode;
3466                 val = fly(parent).getStyle(attr);
3467
3468                 while (parent && this.patterns.transparent.test(val)) {
3469                     parent = parent.parentNode;
3470                     val = fly(parent).getStyle(attr);
3471                     if (parent.tagName.toUpperCase() == 'HTML') {
3472                         val = '#fff';
3473                     }
3474                 }
3475             }
3476         } else {
3477             val = superclass.getAttribute.call(this, attr);
3478         }
3479
3480         return val;
3481     };
3482     proto.getAttribute = function(attr) {
3483         var el = this.getEl();
3484         if (this.patterns.color.test(attr)) {
3485             var val = fly(el).getStyle(attr);
3486
3487             if (this.patterns.transparent.test(val)) {
3488                 var parent = el.parentNode;
3489                 val = fly(parent).getStyle(attr);
3490
3491                 while (parent && this.patterns.transparent.test(val)) {
3492                     parent = parent.parentNode;
3493                     val = fly(parent).getStyle(attr);
3494                     if (parent.tagName.toUpperCase() == 'HTML') {
3495                         val = '#fff';
3496                     }
3497                 }
3498             }
3499         } else {
3500             val = superclass.getAttribute.call(this, attr);
3501         }
3502
3503         return val;
3504     };
3505
3506     proto.doMethod = function(attr, start, end) {
3507         var val;
3508
3509         if (this.patterns.color.test(attr)) {
3510             val = [];
3511             for (var i = 0, len = start.length; i < len; ++i) {
3512                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3513             }
3514
3515             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3516         }
3517         else {
3518             val = superclass.doMethod.call(this, attr, start, end);
3519         }
3520
3521         return val;
3522     };
3523
3524     proto.setRuntimeAttribute = function(attr) {
3525         superclass.setRuntimeAttribute.call(this, attr);
3526
3527         if (this.patterns.color.test(attr)) {
3528             var attributes = this.attributes;
3529             var start = this.parseColor(this.runtimeAttributes[attr].start);
3530             var end = this.parseColor(this.runtimeAttributes[attr].end);
3531
3532             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3533                 end = this.parseColor(attributes[attr].by);
3534
3535                 for (var i = 0, len = start.length; i < len; ++i) {
3536                     end[i] = start[i] + end[i];
3537                 }
3538             }
3539
3540             this.runtimeAttributes[attr].start = start;
3541             this.runtimeAttributes[attr].end = end;
3542         }
3543     };
3544 })();
3545
3546 /*
3547  * Portions of this file are based on pieces of Yahoo User Interface Library
3548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3549  * YUI licensed under the BSD License:
3550  * http://developer.yahoo.net/yui/license.txt
3551  * <script type="text/javascript">
3552  *
3553  */
3554 Roo.lib.Easing = {
3555
3556
3557     easeNone: function (t, b, c, d) {
3558         return c * t / d + b;
3559     },
3560
3561
3562     easeIn: function (t, b, c, d) {
3563         return c * (t /= d) * t + b;
3564     },
3565
3566
3567     easeOut: function (t, b, c, d) {
3568         return -c * (t /= d) * (t - 2) + b;
3569     },
3570
3571
3572     easeBoth: function (t, b, c, d) {
3573         if ((t /= d / 2) < 1) {
3574             return c / 2 * t * t + b;
3575         }
3576
3577         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3578     },
3579
3580
3581     easeInStrong: function (t, b, c, d) {
3582         return c * (t /= d) * t * t * t + b;
3583     },
3584
3585
3586     easeOutStrong: function (t, b, c, d) {
3587         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3588     },
3589
3590
3591     easeBothStrong: function (t, b, c, d) {
3592         if ((t /= d / 2) < 1) {
3593             return c / 2 * t * t * t * t + b;
3594         }
3595
3596         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3597     },
3598
3599
3600
3601     elasticIn: function (t, b, c, d, a, p) {
3602         if (t == 0) {
3603             return b;
3604         }
3605         if ((t /= d) == 1) {
3606             return b + c;
3607         }
3608         if (!p) {
3609             p = d * .3;
3610         }
3611
3612         if (!a || a < Math.abs(c)) {
3613             a = c;
3614             var s = p / 4;
3615         }
3616         else {
3617             var s = p / (2 * Math.PI) * Math.asin(c / a);
3618         }
3619
3620         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3621     },
3622
3623
3624     elasticOut: function (t, b, c, d, a, p) {
3625         if (t == 0) {
3626             return b;
3627         }
3628         if ((t /= d) == 1) {
3629             return b + c;
3630         }
3631         if (!p) {
3632             p = d * .3;
3633         }
3634
3635         if (!a || a < Math.abs(c)) {
3636             a = c;
3637             var s = p / 4;
3638         }
3639         else {
3640             var s = p / (2 * Math.PI) * Math.asin(c / a);
3641         }
3642
3643         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3644     },
3645
3646
3647     elasticBoth: function (t, b, c, d, a, p) {
3648         if (t == 0) {
3649             return b;
3650         }
3651
3652         if ((t /= d / 2) == 2) {
3653             return b + c;
3654         }
3655
3656         if (!p) {
3657             p = d * (.3 * 1.5);
3658         }
3659
3660         if (!a || a < Math.abs(c)) {
3661             a = c;
3662             var s = p / 4;
3663         }
3664         else {
3665             var s = p / (2 * Math.PI) * Math.asin(c / a);
3666         }
3667
3668         if (t < 1) {
3669             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3670                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3671         }
3672         return a * Math.pow(2, -10 * (t -= 1)) *
3673                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3674     },
3675
3676
3677
3678     backIn: function (t, b, c, d, s) {
3679         if (typeof s == 'undefined') {
3680             s = 1.70158;
3681         }
3682         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3683     },
3684
3685
3686     backOut: function (t, b, c, d, s) {
3687         if (typeof s == 'undefined') {
3688             s = 1.70158;
3689         }
3690         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3691     },
3692
3693
3694     backBoth: function (t, b, c, d, s) {
3695         if (typeof s == 'undefined') {
3696             s = 1.70158;
3697         }
3698
3699         if ((t /= d / 2 ) < 1) {
3700             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3701         }
3702         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3703     },
3704
3705
3706     bounceIn: function (t, b, c, d) {
3707         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3708     },
3709
3710
3711     bounceOut: function (t, b, c, d) {
3712         if ((t /= d) < (1 / 2.75)) {
3713             return c * (7.5625 * t * t) + b;
3714         } else if (t < (2 / 2.75)) {
3715             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3716         } else if (t < (2.5 / 2.75)) {
3717             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3718         }
3719         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3720     },
3721
3722
3723     bounceBoth: function (t, b, c, d) {
3724         if (t < d / 2) {
3725             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3726         }
3727         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3728     }
3729 };/*
3730  * Portions of this file are based on pieces of Yahoo User Interface Library
3731  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3732  * YUI licensed under the BSD License:
3733  * http://developer.yahoo.net/yui/license.txt
3734  * <script type="text/javascript">
3735  *
3736  */
3737     (function() {
3738         Roo.lib.Motion = function(el, attributes, duration, method) {
3739             if (el) {
3740                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3741             }
3742         };
3743
3744         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3745
3746
3747         var Y = Roo.lib;
3748         var superclass = Y.Motion.superclass;
3749         var proto = Y.Motion.prototype;
3750
3751         proto.toString = function() {
3752             var el = this.getEl();
3753             var id = el.id || el.tagName;
3754             return ("Motion " + id);
3755         };
3756
3757         proto.patterns.points = /^points$/i;
3758
3759         proto.setAttribute = function(attr, val, unit) {
3760             if (this.patterns.points.test(attr)) {
3761                 unit = unit || 'px';
3762                 superclass.setAttribute.call(this, 'left', val[0], unit);
3763                 superclass.setAttribute.call(this, 'top', val[1], unit);
3764             } else {
3765                 superclass.setAttribute.call(this, attr, val, unit);
3766             }
3767         };
3768
3769         proto.getAttribute = function(attr) {
3770             if (this.patterns.points.test(attr)) {
3771                 var val = [
3772                         superclass.getAttribute.call(this, 'left'),
3773                         superclass.getAttribute.call(this, 'top')
3774                         ];
3775             } else {
3776                 val = superclass.getAttribute.call(this, attr);
3777             }
3778
3779             return val;
3780         };
3781
3782         proto.doMethod = function(attr, start, end) {
3783             var val = null;
3784
3785             if (this.patterns.points.test(attr)) {
3786                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3787                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3788             } else {
3789                 val = superclass.doMethod.call(this, attr, start, end);
3790             }
3791             return val;
3792         };
3793
3794         proto.setRuntimeAttribute = function(attr) {
3795             if (this.patterns.points.test(attr)) {
3796                 var el = this.getEl();
3797                 var attributes = this.attributes;
3798                 var start;
3799                 var control = attributes['points']['control'] || [];
3800                 var end;
3801                 var i, len;
3802
3803                 if (control.length > 0 && !(control[0] instanceof Array)) {
3804                     control = [control];
3805                 } else {
3806                     var tmp = [];
3807                     for (i = 0,len = control.length; i < len; ++i) {
3808                         tmp[i] = control[i];
3809                     }
3810                     control = tmp;
3811                 }
3812
3813                 Roo.fly(el).position();
3814
3815                 if (isset(attributes['points']['from'])) {
3816                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3817                 }
3818                 else {
3819                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3820                 }
3821
3822                 start = this.getAttribute('points');
3823
3824
3825                 if (isset(attributes['points']['to'])) {
3826                     end = translateValues.call(this, attributes['points']['to'], start);
3827
3828                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3829                     for (i = 0,len = control.length; i < len; ++i) {
3830                         control[i] = translateValues.call(this, control[i], start);
3831                     }
3832
3833
3834                 } else if (isset(attributes['points']['by'])) {
3835                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3836
3837                     for (i = 0,len = control.length; i < len; ++i) {
3838                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3839                     }
3840                 }
3841
3842                 this.runtimeAttributes[attr] = [start];
3843
3844                 if (control.length > 0) {
3845                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3846                 }
3847
3848                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3849             }
3850             else {
3851                 superclass.setRuntimeAttribute.call(this, attr);
3852             }
3853         };
3854
3855         var translateValues = function(val, start) {
3856             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3857             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3858
3859             return val;
3860         };
3861
3862         var isset = function(prop) {
3863             return (typeof prop !== 'undefined');
3864         };
3865     })();
3866 /*
3867  * Portions of this file are based on pieces of Yahoo User Interface Library
3868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3869  * YUI licensed under the BSD License:
3870  * http://developer.yahoo.net/yui/license.txt
3871  * <script type="text/javascript">
3872  *
3873  */
3874     (function() {
3875         Roo.lib.Scroll = function(el, attributes, duration, method) {
3876             if (el) {
3877                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3878             }
3879         };
3880
3881         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3882
3883
3884         var Y = Roo.lib;
3885         var superclass = Y.Scroll.superclass;
3886         var proto = Y.Scroll.prototype;
3887
3888         proto.toString = function() {
3889             var el = this.getEl();
3890             var id = el.id || el.tagName;
3891             return ("Scroll " + id);
3892         };
3893
3894         proto.doMethod = function(attr, start, end) {
3895             var val = null;
3896
3897             if (attr == 'scroll') {
3898                 val = [
3899                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3900                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3901                         ];
3902
3903             } else {
3904                 val = superclass.doMethod.call(this, attr, start, end);
3905             }
3906             return val;
3907         };
3908
3909         proto.getAttribute = function(attr) {
3910             var val = null;
3911             var el = this.getEl();
3912
3913             if (attr == 'scroll') {
3914                 val = [ el.scrollLeft, el.scrollTop ];
3915             } else {
3916                 val = superclass.getAttribute.call(this, attr);
3917             }
3918
3919             return val;
3920         };
3921
3922         proto.setAttribute = function(attr, val, unit) {
3923             var el = this.getEl();
3924
3925             if (attr == 'scroll') {
3926                 el.scrollLeft = val[0];
3927                 el.scrollTop = val[1];
3928             } else {
3929                 superclass.setAttribute.call(this, attr, val, unit);
3930             }
3931         };
3932     })();
3933 /*
3934  * Based on:
3935  * Ext JS Library 1.1.1
3936  * Copyright(c) 2006-2007, Ext JS, LLC.
3937  *
3938  * Originally Released Under LGPL - original licence link has changed is not relivant.
3939  *
3940  * Fork - LGPL
3941  * <script type="text/javascript">
3942  */
3943  
3944
3945 /**
3946  * @class Roo.DomHelper
3947  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3948  * 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>.
3949  * @singleton
3950  */
3951 Roo.DomHelper = function(){
3952     var tempTableEl = null;
3953     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3954     var tableRe = /^table|tbody|tr|td$/i;
3955     var xmlns = {};
3956     // build as innerHTML where available
3957     /** @ignore */
3958     var createHtml = function(o){
3959         if(typeof o == 'string'){
3960             return o;
3961         }
3962         var b = "";
3963         if(!o.tag){
3964             o.tag = "div";
3965         }
3966         b += "<" + o.tag;
3967         for(var attr in o){
3968             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3969             if(attr == "style"){
3970                 var s = o["style"];
3971                 if(typeof s == "function"){
3972                     s = s.call();
3973                 }
3974                 if(typeof s == "string"){
3975                     b += ' style="' + s + '"';
3976                 }else if(typeof s == "object"){
3977                     b += ' style="';
3978                     for(var key in s){
3979                         if(typeof s[key] != "function"){
3980                             b += key + ":" + s[key] + ";";
3981                         }
3982                     }
3983                     b += '"';
3984                 }
3985             }else{
3986                 if(attr == "cls"){
3987                     b += ' class="' + o["cls"] + '"';
3988                 }else if(attr == "htmlFor"){
3989                     b += ' for="' + o["htmlFor"] + '"';
3990                 }else{
3991                     b += " " + attr + '="' + o[attr] + '"';
3992                 }
3993             }
3994         }
3995         if(emptyTags.test(o.tag)){
3996             b += "/>";
3997         }else{
3998             b += ">";
3999             var cn = o.children || o.cn;
4000             if(cn){
4001                 //http://bugs.kde.org/show_bug.cgi?id=71506
4002                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4003                     for(var i = 0, len = cn.length; i < len; i++) {
4004                         b += createHtml(cn[i], b);
4005                     }
4006                 }else{
4007                     b += createHtml(cn, b);
4008                 }
4009             }
4010             if(o.html){
4011                 b += o.html;
4012             }
4013             b += "</" + o.tag + ">";
4014         }
4015         return b;
4016     };
4017
4018     // build as dom
4019     /** @ignore */
4020     var createDom = function(o, parentNode){
4021          
4022         // defininition craeted..
4023         var ns = false;
4024         if (o.ns && o.ns != 'html') {
4025                
4026             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4027                 xmlns[o.ns] = o.xmlns;
4028                 ns = o.xmlns;
4029             }
4030             if (typeof(xmlns[o.ns]) == 'undefined') {
4031                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4032             }
4033             ns = xmlns[o.ns];
4034         }
4035         
4036         
4037         if (typeof(o) == 'string') {
4038             return parentNode.appendChild(document.createTextNode(o));
4039         }
4040         o.tag = o.tag || div;
4041         if (o.ns && Roo.isIE) {
4042             ns = false;
4043             o.tag = o.ns + ':' + o.tag;
4044             
4045         }
4046         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4047         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4048         for(var attr in o){
4049             
4050             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4051                     attr == "style" || typeof o[attr] == "function") continue;
4052                     
4053             if(attr=="cls" && Roo.isIE){
4054                 el.className = o["cls"];
4055             }else{
4056                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4057                 else el[attr] = o[attr];
4058             }
4059         }
4060         Roo.DomHelper.applyStyles(el, o.style);
4061         var cn = o.children || o.cn;
4062         if(cn){
4063             //http://bugs.kde.org/show_bug.cgi?id=71506
4064              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4065                 for(var i = 0, len = cn.length; i < len; i++) {
4066                     createDom(cn[i], el);
4067                 }
4068             }else{
4069                 createDom(cn, el);
4070             }
4071         }
4072         if(o.html){
4073             el.innerHTML = o.html;
4074         }
4075         if(parentNode){
4076            parentNode.appendChild(el);
4077         }
4078         return el;
4079     };
4080
4081     var ieTable = function(depth, s, h, e){
4082         tempTableEl.innerHTML = [s, h, e].join('');
4083         var i = -1, el = tempTableEl;
4084         while(++i < depth){
4085             el = el.firstChild;
4086         }
4087         return el;
4088     };
4089
4090     // kill repeat to save bytes
4091     var ts = '<table>',
4092         te = '</table>',
4093         tbs = ts+'<tbody>',
4094         tbe = '</tbody>'+te,
4095         trs = tbs + '<tr>',
4096         tre = '</tr>'+tbe;
4097
4098     /**
4099      * @ignore
4100      * Nasty code for IE's broken table implementation
4101      */
4102     var insertIntoTable = function(tag, where, el, html){
4103         if(!tempTableEl){
4104             tempTableEl = document.createElement('div');
4105         }
4106         var node;
4107         var before = null;
4108         if(tag == 'td'){
4109             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4110                 return;
4111             }
4112             if(where == 'beforebegin'){
4113                 before = el;
4114                 el = el.parentNode;
4115             } else{
4116                 before = el.nextSibling;
4117                 el = el.parentNode;
4118             }
4119             node = ieTable(4, trs, html, tre);
4120         }
4121         else if(tag == 'tr'){
4122             if(where == 'beforebegin'){
4123                 before = el;
4124                 el = el.parentNode;
4125                 node = ieTable(3, tbs, html, tbe);
4126             } else if(where == 'afterend'){
4127                 before = el.nextSibling;
4128                 el = el.parentNode;
4129                 node = ieTable(3, tbs, html, tbe);
4130             } else{ // INTO a TR
4131                 if(where == 'afterbegin'){
4132                     before = el.firstChild;
4133                 }
4134                 node = ieTable(4, trs, html, tre);
4135             }
4136         } else if(tag == 'tbody'){
4137             if(where == 'beforebegin'){
4138                 before = el;
4139                 el = el.parentNode;
4140                 node = ieTable(2, ts, html, te);
4141             } else if(where == 'afterend'){
4142                 before = el.nextSibling;
4143                 el = el.parentNode;
4144                 node = ieTable(2, ts, html, te);
4145             } else{
4146                 if(where == 'afterbegin'){
4147                     before = el.firstChild;
4148                 }
4149                 node = ieTable(3, tbs, html, tbe);
4150             }
4151         } else{ // TABLE
4152             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4153                 return;
4154             }
4155             if(where == 'afterbegin'){
4156                 before = el.firstChild;
4157             }
4158             node = ieTable(2, ts, html, te);
4159         }
4160         el.insertBefore(node, before);
4161         return node;
4162     };
4163
4164     return {
4165     /** True to force the use of DOM instead of html fragments @type Boolean */
4166     useDom : false,
4167
4168     /**
4169      * Returns the markup for the passed Element(s) config
4170      * @param {Object} o The Dom object spec (and children)
4171      * @return {String}
4172      */
4173     markup : function(o){
4174         return createHtml(o);
4175     },
4176
4177     /**
4178      * Applies a style specification to an element
4179      * @param {String/HTMLElement} el The element to apply styles to
4180      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4181      * a function which returns such a specification.
4182      */
4183     applyStyles : function(el, styles){
4184         if(styles){
4185            el = Roo.fly(el);
4186            if(typeof styles == "string"){
4187                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4188                var matches;
4189                while ((matches = re.exec(styles)) != null){
4190                    el.setStyle(matches[1], matches[2]);
4191                }
4192            }else if (typeof styles == "object"){
4193                for (var style in styles){
4194                   el.setStyle(style, styles[style]);
4195                }
4196            }else if (typeof styles == "function"){
4197                 Roo.DomHelper.applyStyles(el, styles.call());
4198            }
4199         }
4200     },
4201
4202     /**
4203      * Inserts an HTML fragment into the Dom
4204      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4205      * @param {HTMLElement} el The context element
4206      * @param {String} html The HTML fragmenet
4207      * @return {HTMLElement} The new node
4208      */
4209     insertHtml : function(where, el, html){
4210         where = where.toLowerCase();
4211         if(el.insertAdjacentHTML){
4212             if(tableRe.test(el.tagName)){
4213                 var rs;
4214                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4215                     return rs;
4216                 }
4217             }
4218             switch(where){
4219                 case "beforebegin":
4220                     el.insertAdjacentHTML('BeforeBegin', html);
4221                     return el.previousSibling;
4222                 case "afterbegin":
4223                     el.insertAdjacentHTML('AfterBegin', html);
4224                     return el.firstChild;
4225                 case "beforeend":
4226                     el.insertAdjacentHTML('BeforeEnd', html);
4227                     return el.lastChild;
4228                 case "afterend":
4229                     el.insertAdjacentHTML('AfterEnd', html);
4230                     return el.nextSibling;
4231             }
4232             throw 'Illegal insertion point -> "' + where + '"';
4233         }
4234         var range = el.ownerDocument.createRange();
4235         var frag;
4236         switch(where){
4237              case "beforebegin":
4238                 range.setStartBefore(el);
4239                 frag = range.createContextualFragment(html);
4240                 el.parentNode.insertBefore(frag, el);
4241                 return el.previousSibling;
4242              case "afterbegin":
4243                 if(el.firstChild){
4244                     range.setStartBefore(el.firstChild);
4245                     frag = range.createContextualFragment(html);
4246                     el.insertBefore(frag, el.firstChild);
4247                     return el.firstChild;
4248                 }else{
4249                     el.innerHTML = html;
4250                     return el.firstChild;
4251                 }
4252             case "beforeend":
4253                 if(el.lastChild){
4254                     range.setStartAfter(el.lastChild);
4255                     frag = range.createContextualFragment(html);
4256                     el.appendChild(frag);
4257                     return el.lastChild;
4258                 }else{
4259                     el.innerHTML = html;
4260                     return el.lastChild;
4261                 }
4262             case "afterend":
4263                 range.setStartAfter(el);
4264                 frag = range.createContextualFragment(html);
4265                 el.parentNode.insertBefore(frag, el.nextSibling);
4266                 return el.nextSibling;
4267             }
4268             throw 'Illegal insertion point -> "' + where + '"';
4269     },
4270
4271     /**
4272      * Creates new Dom element(s) and inserts them before el
4273      * @param {String/HTMLElement/Element} el The context element
4274      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4275      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4276      * @return {HTMLElement/Roo.Element} The new node
4277      */
4278     insertBefore : function(el, o, returnElement){
4279         return this.doInsert(el, o, returnElement, "beforeBegin");
4280     },
4281
4282     /**
4283      * Creates new Dom element(s) and inserts them after el
4284      * @param {String/HTMLElement/Element} el The context element
4285      * @param {Object} o The Dom object spec (and children)
4286      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4287      * @return {HTMLElement/Roo.Element} The new node
4288      */
4289     insertAfter : function(el, o, returnElement){
4290         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4291     },
4292
4293     /**
4294      * Creates new Dom element(s) and inserts them as the first child of el
4295      * @param {String/HTMLElement/Element} el The context element
4296      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4297      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4298      * @return {HTMLElement/Roo.Element} The new node
4299      */
4300     insertFirst : function(el, o, returnElement){
4301         return this.doInsert(el, o, returnElement, "afterBegin");
4302     },
4303
4304     // private
4305     doInsert : function(el, o, returnElement, pos, sibling){
4306         el = Roo.getDom(el);
4307         var newNode;
4308         if(this.useDom || o.ns){
4309             newNode = createDom(o, null);
4310             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4311         }else{
4312             var html = createHtml(o);
4313             newNode = this.insertHtml(pos, el, html);
4314         }
4315         return returnElement ? Roo.get(newNode, true) : newNode;
4316     },
4317
4318     /**
4319      * Creates new Dom element(s) and appends them to el
4320      * @param {String/HTMLElement/Element} el The context element
4321      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4322      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4323      * @return {HTMLElement/Roo.Element} The new node
4324      */
4325     append : function(el, o, returnElement){
4326         el = Roo.getDom(el);
4327         var newNode;
4328         if(this.useDom || o.ns){
4329             newNode = createDom(o, null);
4330             el.appendChild(newNode);
4331         }else{
4332             var html = createHtml(o);
4333             newNode = this.insertHtml("beforeEnd", el, html);
4334         }
4335         return returnElement ? Roo.get(newNode, true) : newNode;
4336     },
4337
4338     /**
4339      * Creates new Dom element(s) and overwrites the contents of el with them
4340      * @param {String/HTMLElement/Element} el The context element
4341      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4342      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4343      * @return {HTMLElement/Roo.Element} The new node
4344      */
4345     overwrite : function(el, o, returnElement){
4346         el = Roo.getDom(el);
4347         if (o.ns) {
4348           
4349             while (el.childNodes.length) {
4350                 el.removeChild(el.firstChild);
4351             }
4352             createDom(o, el);
4353         } else {
4354             el.innerHTML = createHtml(o);   
4355         }
4356         
4357         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4358     },
4359
4360     /**
4361      * Creates a new Roo.DomHelper.Template from the Dom object spec
4362      * @param {Object} o The Dom object spec (and children)
4363      * @return {Roo.DomHelper.Template} The new template
4364      */
4365     createTemplate : function(o){
4366         var html = createHtml(o);
4367         return new Roo.Template(html);
4368     }
4369     };
4370 }();
4371 /*
4372  * Based on:
4373  * Ext JS Library 1.1.1
4374  * Copyright(c) 2006-2007, Ext JS, LLC.
4375  *
4376  * Originally Released Under LGPL - original licence link has changed is not relivant.
4377  *
4378  * Fork - LGPL
4379  * <script type="text/javascript">
4380  */
4381  
4382 /**
4383 * @class Roo.Template
4384 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4385 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4386 * Usage:
4387 <pre><code>
4388 var t = new Roo.Template(
4389     '&lt;div name="{id}"&gt;',
4390         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4391     '&lt;/div&gt;'
4392 );
4393 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4394 </code></pre>
4395 * 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>. 
4396 * @constructor
4397 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4398 */
4399 Roo.Template = function(html){
4400     if(html instanceof Array){
4401         html = html.join("");
4402     }else if(arguments.length > 1){
4403         html = Array.prototype.join.call(arguments, "");
4404     }
4405     /**@private*/
4406     this.html = html;
4407     
4408 };
4409 Roo.Template.prototype = {
4410     /**
4411      * Returns an HTML fragment of this template with the specified values applied.
4412      * @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'})
4413      * @return {String} The HTML fragment
4414      */
4415     applyTemplate : function(values){
4416         if(this.compiled){
4417             return this.compiled(values);
4418         }
4419         var useF = this.disableFormats !== true;
4420         var fm = Roo.util.Format, tpl = this;
4421         var fn = function(m, name, format, args){
4422             if(format && useF){
4423                 if(format.substr(0, 5) == "this."){
4424                     return tpl.call(format.substr(5), values[name], values);
4425                 }else{
4426                     if(args){
4427                         // quoted values are required for strings in compiled templates, 
4428                         // but for non compiled we need to strip them
4429                         // quoted reversed for jsmin
4430                         var re = /^\s*['"](.*)["']\s*$/;
4431                         args = args.split(',');
4432                         for(var i = 0, len = args.length; i < len; i++){
4433                             args[i] = args[i].replace(re, "$1");
4434                         }
4435                         args = [values[name]].concat(args);
4436                     }else{
4437                         args = [values[name]];
4438                     }
4439                     return fm[format].apply(fm, args);
4440                 }
4441             }else{
4442                 return values[name] !== undefined ? values[name] : "";
4443             }
4444         };
4445         return this.html.replace(this.re, fn);
4446     },
4447     
4448     /**
4449      * Sets the HTML used as the template and optionally compiles it.
4450      * @param {String} html
4451      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4452      * @return {Roo.Template} this
4453      */
4454     set : function(html, compile){
4455         this.html = html;
4456         this.compiled = null;
4457         if(compile){
4458             this.compile();
4459         }
4460         return this;
4461     },
4462     
4463     /**
4464      * True to disable format functions (defaults to false)
4465      * @type Boolean
4466      */
4467     disableFormats : false,
4468     
4469     /**
4470     * The regular expression used to match template variables 
4471     * @type RegExp
4472     * @property 
4473     */
4474     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4475     
4476     /**
4477      * Compiles the template into an internal function, eliminating the RegEx overhead.
4478      * @return {Roo.Template} this
4479      */
4480     compile : function(){
4481         var fm = Roo.util.Format;
4482         var useF = this.disableFormats !== true;
4483         var sep = Roo.isGecko ? "+" : ",";
4484         var fn = function(m, name, format, args){
4485             if(format && useF){
4486                 args = args ? ',' + args : "";
4487                 if(format.substr(0, 5) != "this."){
4488                     format = "fm." + format + '(';
4489                 }else{
4490                     format = 'this.call("'+ format.substr(5) + '", ';
4491                     args = ", values";
4492                 }
4493             }else{
4494                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4495             }
4496             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4497         };
4498         var body;
4499         // branched to use + in gecko and [].join() in others
4500         if(Roo.isGecko){
4501             body = "this.compiled = function(values){ return '" +
4502                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4503                     "';};";
4504         }else{
4505             body = ["this.compiled = function(values){ return ['"];
4506             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4507             body.push("'].join('');};");
4508             body = body.join('');
4509         }
4510         /**
4511          * eval:var:values
4512          * eval:var:fm
4513          */
4514         eval(body);
4515         return this;
4516     },
4517     
4518     // private function used to call members
4519     call : function(fnName, value, allValues){
4520         return this[fnName](value, allValues);
4521     },
4522     
4523     /**
4524      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4525      * @param {String/HTMLElement/Roo.Element} el The context element
4526      * @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'})
4527      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4528      * @return {HTMLElement/Roo.Element} The new node or Element
4529      */
4530     insertFirst: function(el, values, returnElement){
4531         return this.doInsert('afterBegin', el, values, returnElement);
4532     },
4533
4534     /**
4535      * Applies the supplied values to the template and inserts the new node(s) before el.
4536      * @param {String/HTMLElement/Roo.Element} el The context element
4537      * @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'})
4538      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4539      * @return {HTMLElement/Roo.Element} The new node or Element
4540      */
4541     insertBefore: function(el, values, returnElement){
4542         return this.doInsert('beforeBegin', el, values, returnElement);
4543     },
4544
4545     /**
4546      * Applies the supplied values to the template and inserts the new node(s) after el.
4547      * @param {String/HTMLElement/Roo.Element} el The context element
4548      * @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'})
4549      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4550      * @return {HTMLElement/Roo.Element} The new node or Element
4551      */
4552     insertAfter : function(el, values, returnElement){
4553         return this.doInsert('afterEnd', el, values, returnElement);
4554     },
4555     
4556     /**
4557      * Applies the supplied values to the template and appends the new node(s) to el.
4558      * @param {String/HTMLElement/Roo.Element} el The context element
4559      * @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'})
4560      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4561      * @return {HTMLElement/Roo.Element} The new node or Element
4562      */
4563     append : function(el, values, returnElement){
4564         return this.doInsert('beforeEnd', el, values, returnElement);
4565     },
4566
4567     doInsert : function(where, el, values, returnEl){
4568         el = Roo.getDom(el);
4569         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4570         return returnEl ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4575      * @param {String/HTMLElement/Roo.Element} el The context element
4576      * @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'})
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4578      * @return {HTMLElement/Roo.Element} The new node or Element
4579      */
4580     overwrite : function(el, values, returnElement){
4581         el = Roo.getDom(el);
4582         el.innerHTML = this.applyTemplate(values);
4583         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4584     }
4585 };
4586 /**
4587  * Alias for {@link #applyTemplate}
4588  * @method
4589  */
4590 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4591
4592 // backwards compat
4593 Roo.DomHelper.Template = Roo.Template;
4594
4595 /**
4596  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4597  * @param {String/HTMLElement} el A DOM element or its id
4598  * @returns {Roo.Template} The created template
4599  * @static
4600  */
4601 Roo.Template.from = function(el){
4602     el = Roo.getDom(el);
4603     return new Roo.Template(el.value || el.innerHTML);
4604 };/*
4605  * Based on:
4606  * Ext JS Library 1.1.1
4607  * Copyright(c) 2006-2007, Ext JS, LLC.
4608  *
4609  * Originally Released Under LGPL - original licence link has changed is not relivant.
4610  *
4611  * Fork - LGPL
4612  * <script type="text/javascript">
4613  */
4614  
4615
4616 /*
4617  * This is code is also distributed under MIT license for use
4618  * with jQuery and prototype JavaScript libraries.
4619  */
4620 /**
4621  * @class Roo.DomQuery
4622 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).
4623 <p>
4624 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>
4625
4626 <p>
4627 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.
4628 </p>
4629 <h4>Element Selectors:</h4>
4630 <ul class="list">
4631     <li> <b>*</b> any element</li>
4632     <li> <b>E</b> an element with the tag E</li>
4633     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4634     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4635     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4636     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4637 </ul>
4638 <h4>Attribute Selectors:</h4>
4639 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4640 <ul class="list">
4641     <li> <b>E[foo]</b> has an attribute "foo"</li>
4642     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4643     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4644     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4645     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4646     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4647     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4648 </ul>
4649 <h4>Pseudo Classes:</h4>
4650 <ul class="list">
4651     <li> <b>E:first-child</b> E is the first child of its parent</li>
4652     <li> <b>E:last-child</b> E is the last child of its parent</li>
4653     <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>
4654     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4655     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4656     <li> <b>E:only-child</b> E is the only child of its parent</li>
4657     <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>
4658     <li> <b>E:first</b> the first E in the resultset</li>
4659     <li> <b>E:last</b> the last E in the resultset</li>
4660     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4661     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4662     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4663     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4664     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4665     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4666     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4667     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4668     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4669 </ul>
4670 <h4>CSS Value Selectors:</h4>
4671 <ul class="list">
4672     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4673     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4674     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4675     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4676     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4677     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4678 </ul>
4679  * @singleton
4680  */
4681 Roo.DomQuery = function(){
4682     var cache = {}, simpleCache = {}, valueCache = {};
4683     var nonSpace = /\S/;
4684     var trimRe = /^\s+|\s+$/g;
4685     var tplRe = /\{(\d+)\}/g;
4686     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4687     var tagTokenRe = /^(#)?([\w-\*]+)/;
4688     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4689
4690     function child(p, index){
4691         var i = 0;
4692         var n = p.firstChild;
4693         while(n){
4694             if(n.nodeType == 1){
4695                if(++i == index){
4696                    return n;
4697                }
4698             }
4699             n = n.nextSibling;
4700         }
4701         return null;
4702     };
4703
4704     function next(n){
4705         while((n = n.nextSibling) && n.nodeType != 1);
4706         return n;
4707     };
4708
4709     function prev(n){
4710         while((n = n.previousSibling) && n.nodeType != 1);
4711         return n;
4712     };
4713
4714     function children(d){
4715         var n = d.firstChild, ni = -1;
4716             while(n){
4717                 var nx = n.nextSibling;
4718                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4719                     d.removeChild(n);
4720                 }else{
4721                     n.nodeIndex = ++ni;
4722                 }
4723                 n = nx;
4724             }
4725             return this;
4726         };
4727
4728     function byClassName(c, a, v){
4729         if(!v){
4730             return c;
4731         }
4732         var r = [], ri = -1, cn;
4733         for(var i = 0, ci; ci = c[i]; i++){
4734             if((' '+ci.className+' ').indexOf(v) != -1){
4735                 r[++ri] = ci;
4736             }
4737         }
4738         return r;
4739     };
4740
4741     function attrValue(n, attr){
4742         if(!n.tagName && typeof n.length != "undefined"){
4743             n = n[0];
4744         }
4745         if(!n){
4746             return null;
4747         }
4748         if(attr == "for"){
4749             return n.htmlFor;
4750         }
4751         if(attr == "class" || attr == "className"){
4752             return n.className;
4753         }
4754         return n.getAttribute(attr) || n[attr];
4755
4756     };
4757
4758     function getNodes(ns, mode, tagName){
4759         var result = [], ri = -1, cs;
4760         if(!ns){
4761             return result;
4762         }
4763         tagName = tagName || "*";
4764         if(typeof ns.getElementsByTagName != "undefined"){
4765             ns = [ns];
4766         }
4767         if(!mode){
4768             for(var i = 0, ni; ni = ns[i]; i++){
4769                 cs = ni.getElementsByTagName(tagName);
4770                 for(var j = 0, ci; ci = cs[j]; j++){
4771                     result[++ri] = ci;
4772                 }
4773             }
4774         }else if(mode == "/" || mode == ">"){
4775             var utag = tagName.toUpperCase();
4776             for(var i = 0, ni, cn; ni = ns[i]; i++){
4777                 cn = ni.children || ni.childNodes;
4778                 for(var j = 0, cj; cj = cn[j]; j++){
4779                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4780                         result[++ri] = cj;
4781                     }
4782                 }
4783             }
4784         }else if(mode == "+"){
4785             var utag = tagName.toUpperCase();
4786             for(var i = 0, n; n = ns[i]; i++){
4787                 while((n = n.nextSibling) && n.nodeType != 1);
4788                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4789                     result[++ri] = n;
4790                 }
4791             }
4792         }else if(mode == "~"){
4793             for(var i = 0, n; n = ns[i]; i++){
4794                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4795                 if(n){
4796                     result[++ri] = n;
4797                 }
4798             }
4799         }
4800         return result;
4801     };
4802
4803     function concat(a, b){
4804         if(b.slice){
4805             return a.concat(b);
4806         }
4807         for(var i = 0, l = b.length; i < l; i++){
4808             a[a.length] = b[i];
4809         }
4810         return a;
4811     }
4812
4813     function byTag(cs, tagName){
4814         if(cs.tagName || cs == document){
4815             cs = [cs];
4816         }
4817         if(!tagName){
4818             return cs;
4819         }
4820         var r = [], ri = -1;
4821         tagName = tagName.toLowerCase();
4822         for(var i = 0, ci; ci = cs[i]; i++){
4823             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4824                 r[++ri] = ci;
4825             }
4826         }
4827         return r;
4828     };
4829
4830     function byId(cs, attr, id){
4831         if(cs.tagName || cs == document){
4832             cs = [cs];
4833         }
4834         if(!id){
4835             return cs;
4836         }
4837         var r = [], ri = -1;
4838         for(var i = 0,ci; ci = cs[i]; i++){
4839             if(ci && ci.id == id){
4840                 r[++ri] = ci;
4841                 return r;
4842             }
4843         }
4844         return r;
4845     };
4846
4847     function byAttribute(cs, attr, value, op, custom){
4848         var r = [], ri = -1, st = custom=="{";
4849         var f = Roo.DomQuery.operators[op];
4850         for(var i = 0, ci; ci = cs[i]; i++){
4851             var a;
4852             if(st){
4853                 a = Roo.DomQuery.getStyle(ci, attr);
4854             }
4855             else if(attr == "class" || attr == "className"){
4856                 a = ci.className;
4857             }else if(attr == "for"){
4858                 a = ci.htmlFor;
4859             }else if(attr == "href"){
4860                 a = ci.getAttribute("href", 2);
4861             }else{
4862                 a = ci.getAttribute(attr);
4863             }
4864             if((f && f(a, value)) || (!f && a)){
4865                 r[++ri] = ci;
4866             }
4867         }
4868         return r;
4869     };
4870
4871     function byPseudo(cs, name, value){
4872         return Roo.DomQuery.pseudos[name](cs, value);
4873     };
4874
4875     // This is for IE MSXML which does not support expandos.
4876     // IE runs the same speed using setAttribute, however FF slows way down
4877     // and Safari completely fails so they need to continue to use expandos.
4878     var isIE = window.ActiveXObject ? true : false;
4879
4880     // this eval is stop the compressor from
4881     // renaming the variable to something shorter
4882     
4883     /** eval:var:batch */
4884     var batch = 30803; 
4885
4886     var key = 30803;
4887
4888     function nodupIEXml(cs){
4889         var d = ++key;
4890         cs[0].setAttribute("_nodup", d);
4891         var r = [cs[0]];
4892         for(var i = 1, len = cs.length; i < len; i++){
4893             var c = cs[i];
4894             if(!c.getAttribute("_nodup") != d){
4895                 c.setAttribute("_nodup", d);
4896                 r[r.length] = c;
4897             }
4898         }
4899         for(var i = 0, len = cs.length; i < len; i++){
4900             cs[i].removeAttribute("_nodup");
4901         }
4902         return r;
4903     }
4904
4905     function nodup(cs){
4906         if(!cs){
4907             return [];
4908         }
4909         var len = cs.length, c, i, r = cs, cj, ri = -1;
4910         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4911             return cs;
4912         }
4913         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4914             return nodupIEXml(cs);
4915         }
4916         var d = ++key;
4917         cs[0]._nodup = d;
4918         for(i = 1; c = cs[i]; i++){
4919             if(c._nodup != d){
4920                 c._nodup = d;
4921             }else{
4922                 r = [];
4923                 for(var j = 0; j < i; j++){
4924                     r[++ri] = cs[j];
4925                 }
4926                 for(j = i+1; cj = cs[j]; j++){
4927                     if(cj._nodup != d){
4928                         cj._nodup = d;
4929                         r[++ri] = cj;
4930                     }
4931                 }
4932                 return r;
4933             }
4934         }
4935         return r;
4936     }
4937
4938     function quickDiffIEXml(c1, c2){
4939         var d = ++key;
4940         for(var i = 0, len = c1.length; i < len; i++){
4941             c1[i].setAttribute("_qdiff", d);
4942         }
4943         var r = [];
4944         for(var i = 0, len = c2.length; i < len; i++){
4945             if(c2[i].getAttribute("_qdiff") != d){
4946                 r[r.length] = c2[i];
4947             }
4948         }
4949         for(var i = 0, len = c1.length; i < len; i++){
4950            c1[i].removeAttribute("_qdiff");
4951         }
4952         return r;
4953     }
4954
4955     function quickDiff(c1, c2){
4956         var len1 = c1.length;
4957         if(!len1){
4958             return c2;
4959         }
4960         if(isIE && c1[0].selectSingleNode){
4961             return quickDiffIEXml(c1, c2);
4962         }
4963         var d = ++key;
4964         for(var i = 0; i < len1; i++){
4965             c1[i]._qdiff = d;
4966         }
4967         var r = [];
4968         for(var i = 0, len = c2.length; i < len; i++){
4969             if(c2[i]._qdiff != d){
4970                 r[r.length] = c2[i];
4971             }
4972         }
4973         return r;
4974     }
4975
4976     function quickId(ns, mode, root, id){
4977         if(ns == root){
4978            var d = root.ownerDocument || root;
4979            return d.getElementById(id);
4980         }
4981         ns = getNodes(ns, mode, "*");
4982         return byId(ns, null, id);
4983     }
4984
4985     return {
4986         getStyle : function(el, name){
4987             return Roo.fly(el).getStyle(name);
4988         },
4989         /**
4990          * Compiles a selector/xpath query into a reusable function. The returned function
4991          * takes one parameter "root" (optional), which is the context node from where the query should start.
4992          * @param {String} selector The selector/xpath query
4993          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4994          * @return {Function}
4995          */
4996         compile : function(path, type){
4997             type = type || "select";
4998             
4999             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5000             var q = path, mode, lq;
5001             var tk = Roo.DomQuery.matchers;
5002             var tklen = tk.length;
5003             var mm;
5004
5005             // accept leading mode switch
5006             var lmode = q.match(modeRe);
5007             if(lmode && lmode[1]){
5008                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5009                 q = q.replace(lmode[1], "");
5010             }
5011             // strip leading slashes
5012             while(path.substr(0, 1)=="/"){
5013                 path = path.substr(1);
5014             }
5015
5016             while(q && lq != q){
5017                 lq = q;
5018                 var tm = q.match(tagTokenRe);
5019                 if(type == "select"){
5020                     if(tm){
5021                         if(tm[1] == "#"){
5022                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5023                         }else{
5024                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5025                         }
5026                         q = q.replace(tm[0], "");
5027                     }else if(q.substr(0, 1) != '@'){
5028                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5029                     }
5030                 }else{
5031                     if(tm){
5032                         if(tm[1] == "#"){
5033                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5034                         }else{
5035                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5036                         }
5037                         q = q.replace(tm[0], "");
5038                     }
5039                 }
5040                 while(!(mm = q.match(modeRe))){
5041                     var matched = false;
5042                     for(var j = 0; j < tklen; j++){
5043                         var t = tk[j];
5044                         var m = q.match(t.re);
5045                         if(m){
5046                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5047                                                     return m[i];
5048                                                 });
5049                             q = q.replace(m[0], "");
5050                             matched = true;
5051                             break;
5052                         }
5053                     }
5054                     // prevent infinite loop on bad selector
5055                     if(!matched){
5056                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5057                     }
5058                 }
5059                 if(mm[1]){
5060                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5061                     q = q.replace(mm[1], "");
5062                 }
5063             }
5064             fn[fn.length] = "return nodup(n);\n}";
5065             
5066              /** 
5067               * list of variables that need from compression as they are used by eval.
5068              *  eval:var:batch 
5069              *  eval:var:nodup
5070              *  eval:var:byTag
5071              *  eval:var:ById
5072              *  eval:var:getNodes
5073              *  eval:var:quickId
5074              *  eval:var:mode
5075              *  eval:var:root
5076              *  eval:var:n
5077              *  eval:var:byClassName
5078              *  eval:var:byPseudo
5079              *  eval:var:byAttribute
5080              *  eval:var:attrValue
5081              * 
5082              **/ 
5083             eval(fn.join(""));
5084             return f;
5085         },
5086
5087         /**
5088          * Selects a group of elements.
5089          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5090          * @param {Node} root (optional) The start of the query (defaults to document).
5091          * @return {Array}
5092          */
5093         select : function(path, root, type){
5094             if(!root || root == document){
5095                 root = document;
5096             }
5097             if(typeof root == "string"){
5098                 root = document.getElementById(root);
5099             }
5100             var paths = path.split(",");
5101             var results = [];
5102             for(var i = 0, len = paths.length; i < len; i++){
5103                 var p = paths[i].replace(trimRe, "");
5104                 if(!cache[p]){
5105                     cache[p] = Roo.DomQuery.compile(p);
5106                     if(!cache[p]){
5107                         throw p + " is not a valid selector";
5108                     }
5109                 }
5110                 var result = cache[p](root);
5111                 if(result && result != document){
5112                     results = results.concat(result);
5113                 }
5114             }
5115             if(paths.length > 1){
5116                 return nodup(results);
5117             }
5118             return results;
5119         },
5120
5121         /**
5122          * Selects a single element.
5123          * @param {String} selector The selector/xpath query
5124          * @param {Node} root (optional) The start of the query (defaults to document).
5125          * @return {Element}
5126          */
5127         selectNode : function(path, root){
5128             return Roo.DomQuery.select(path, root)[0];
5129         },
5130
5131         /**
5132          * Selects the value of a node, optionally replacing null with the defaultValue.
5133          * @param {String} selector The selector/xpath query
5134          * @param {Node} root (optional) The start of the query (defaults to document).
5135          * @param {String} defaultValue
5136          */
5137         selectValue : function(path, root, defaultValue){
5138             path = path.replace(trimRe, "");
5139             if(!valueCache[path]){
5140                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5141             }
5142             var n = valueCache[path](root);
5143             n = n[0] ? n[0] : n;
5144             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5145             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5146         },
5147
5148         /**
5149          * Selects the value of a node, parsing integers and floats.
5150          * @param {String} selector The selector/xpath query
5151          * @param {Node} root (optional) The start of the query (defaults to document).
5152          * @param {Number} defaultValue
5153          * @return {Number}
5154          */
5155         selectNumber : function(path, root, defaultValue){
5156             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5157             return parseFloat(v);
5158         },
5159
5160         /**
5161          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5162          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5163          * @param {String} selector The simple selector to test
5164          * @return {Boolean}
5165          */
5166         is : function(el, ss){
5167             if(typeof el == "string"){
5168                 el = document.getElementById(el);
5169             }
5170             var isArray = (el instanceof Array);
5171             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5172             return isArray ? (result.length == el.length) : (result.length > 0);
5173         },
5174
5175         /**
5176          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5177          * @param {Array} el An array of elements to filter
5178          * @param {String} selector The simple selector to test
5179          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5180          * the selector instead of the ones that match
5181          * @return {Array}
5182          */
5183         filter : function(els, ss, nonMatches){
5184             ss = ss.replace(trimRe, "");
5185             if(!simpleCache[ss]){
5186                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5187             }
5188             var result = simpleCache[ss](els);
5189             return nonMatches ? quickDiff(result, els) : result;
5190         },
5191
5192         /**
5193          * Collection of matching regular expressions and code snippets.
5194          */
5195         matchers : [{
5196                 re: /^\.([\w-]+)/,
5197                 select: 'n = byClassName(n, null, " {1} ");'
5198             }, {
5199                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5200                 select: 'n = byPseudo(n, "{1}", "{2}");'
5201             },{
5202                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5203                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5204             }, {
5205                 re: /^#([\w-]+)/,
5206                 select: 'n = byId(n, null, "{1}");'
5207             },{
5208                 re: /^@([\w-]+)/,
5209                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5210             }
5211         ],
5212
5213         /**
5214          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5215          * 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;.
5216          */
5217         operators : {
5218             "=" : function(a, v){
5219                 return a == v;
5220             },
5221             "!=" : function(a, v){
5222                 return a != v;
5223             },
5224             "^=" : function(a, v){
5225                 return a && a.substr(0, v.length) == v;
5226             },
5227             "$=" : function(a, v){
5228                 return a && a.substr(a.length-v.length) == v;
5229             },
5230             "*=" : function(a, v){
5231                 return a && a.indexOf(v) !== -1;
5232             },
5233             "%=" : function(a, v){
5234                 return (a % v) == 0;
5235             },
5236             "|=" : function(a, v){
5237                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5238             },
5239             "~=" : function(a, v){
5240                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5241             }
5242         },
5243
5244         /**
5245          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5246          * and the argument (if any) supplied in the selector.
5247          */
5248         pseudos : {
5249             "first-child" : function(c){
5250                 var r = [], ri = -1, n;
5251                 for(var i = 0, ci; ci = n = c[i]; i++){
5252                     while((n = n.previousSibling) && n.nodeType != 1);
5253                     if(!n){
5254                         r[++ri] = ci;
5255                     }
5256                 }
5257                 return r;
5258             },
5259
5260             "last-child" : function(c){
5261                 var r = [], ri = -1, n;
5262                 for(var i = 0, ci; ci = n = c[i]; i++){
5263                     while((n = n.nextSibling) && n.nodeType != 1);
5264                     if(!n){
5265                         r[++ri] = ci;
5266                     }
5267                 }
5268                 return r;
5269             },
5270
5271             "nth-child" : function(c, a) {
5272                 var r = [], ri = -1;
5273                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5274                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5275                 for(var i = 0, n; n = c[i]; i++){
5276                     var pn = n.parentNode;
5277                     if (batch != pn._batch) {
5278                         var j = 0;
5279                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5280                             if(cn.nodeType == 1){
5281                                cn.nodeIndex = ++j;
5282                             }
5283                         }
5284                         pn._batch = batch;
5285                     }
5286                     if (f == 1) {
5287                         if (l == 0 || n.nodeIndex == l){
5288                             r[++ri] = n;
5289                         }
5290                     } else if ((n.nodeIndex + l) % f == 0){
5291                         r[++ri] = n;
5292                     }
5293                 }
5294
5295                 return r;
5296             },
5297
5298             "only-child" : function(c){
5299                 var r = [], ri = -1;;
5300                 for(var i = 0, ci; ci = c[i]; i++){
5301                     if(!prev(ci) && !next(ci)){
5302                         r[++ri] = ci;
5303                     }
5304                 }
5305                 return r;
5306             },
5307
5308             "empty" : function(c){
5309                 var r = [], ri = -1;
5310                 for(var i = 0, ci; ci = c[i]; i++){
5311                     var cns = ci.childNodes, j = 0, cn, empty = true;
5312                     while(cn = cns[j]){
5313                         ++j;
5314                         if(cn.nodeType == 1 || cn.nodeType == 3){
5315                             empty = false;
5316                             break;
5317                         }
5318                     }
5319                     if(empty){
5320                         r[++ri] = ci;
5321                     }
5322                 }
5323                 return r;
5324             },
5325
5326             "contains" : function(c, v){
5327                 var r = [], ri = -1;
5328                 for(var i = 0, ci; ci = c[i]; i++){
5329                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5330                         r[++ri] = ci;
5331                     }
5332                 }
5333                 return r;
5334             },
5335
5336             "nodeValue" : function(c, v){
5337                 var r = [], ri = -1;
5338                 for(var i = 0, ci; ci = c[i]; i++){
5339                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5340                         r[++ri] = ci;
5341                     }
5342                 }
5343                 return r;
5344             },
5345
5346             "checked" : function(c){
5347                 var r = [], ri = -1;
5348                 for(var i = 0, ci; ci = c[i]; i++){
5349                     if(ci.checked == true){
5350                         r[++ri] = ci;
5351                     }
5352                 }
5353                 return r;
5354             },
5355
5356             "not" : function(c, ss){
5357                 return Roo.DomQuery.filter(c, ss, true);
5358             },
5359
5360             "odd" : function(c){
5361                 return this["nth-child"](c, "odd");
5362             },
5363
5364             "even" : function(c){
5365                 return this["nth-child"](c, "even");
5366             },
5367
5368             "nth" : function(c, a){
5369                 return c[a-1] || [];
5370             },
5371
5372             "first" : function(c){
5373                 return c[0] || [];
5374             },
5375
5376             "last" : function(c){
5377                 return c[c.length-1] || [];
5378             },
5379
5380             "has" : function(c, ss){
5381                 var s = Roo.DomQuery.select;
5382                 var r = [], ri = -1;
5383                 for(var i = 0, ci; ci = c[i]; i++){
5384                     if(s(ss, ci).length > 0){
5385                         r[++ri] = ci;
5386                     }
5387                 }
5388                 return r;
5389             },
5390
5391             "next" : function(c, ss){
5392                 var is = Roo.DomQuery.is;
5393                 var r = [], ri = -1;
5394                 for(var i = 0, ci; ci = c[i]; i++){
5395                     var n = next(ci);
5396                     if(n && is(n, ss)){
5397                         r[++ri] = ci;
5398                     }
5399                 }
5400                 return r;
5401             },
5402
5403             "prev" : function(c, ss){
5404                 var is = Roo.DomQuery.is;
5405                 var r = [], ri = -1;
5406                 for(var i = 0, ci; ci = c[i]; i++){
5407                     var n = prev(ci);
5408                     if(n && is(n, ss)){
5409                         r[++ri] = ci;
5410                     }
5411                 }
5412                 return r;
5413             }
5414         }
5415     };
5416 }();
5417
5418 /**
5419  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5420  * @param {String} path The selector/xpath query
5421  * @param {Node} root (optional) The start of the query (defaults to document).
5422  * @return {Array}
5423  * @member Roo
5424  * @method query
5425  */
5426 Roo.query = Roo.DomQuery.select;
5427 /*
5428  * Based on:
5429  * Ext JS Library 1.1.1
5430  * Copyright(c) 2006-2007, Ext JS, LLC.
5431  *
5432  * Originally Released Under LGPL - original licence link has changed is not relivant.
5433  *
5434  * Fork - LGPL
5435  * <script type="text/javascript">
5436  */
5437
5438 /**
5439  * @class Roo.util.Observable
5440  * Base class that provides a common interface for publishing events. Subclasses are expected to
5441  * to have a property "events" with all the events defined.<br>
5442  * For example:
5443  * <pre><code>
5444  Employee = function(name){
5445     this.name = name;
5446     this.addEvents({
5447         "fired" : true,
5448         "quit" : true
5449     });
5450  }
5451  Roo.extend(Employee, Roo.util.Observable);
5452 </code></pre>
5453  * @param {Object} config properties to use (incuding events / listeners)
5454  */
5455
5456 Roo.util.Observable = function(cfg){
5457     
5458     cfg = cfg|| {};
5459     this.addEvents(cfg.events || {});
5460     if (cfg.events) {
5461         delete cfg.events; // make sure
5462     }
5463      
5464     Roo.apply(this, cfg);
5465     
5466     if(this.listeners){
5467         this.on(this.listeners);
5468         delete this.listeners;
5469     }
5470 };
5471 Roo.util.Observable.prototype = {
5472     /** 
5473  * @cfg {Object} listeners  list of events and functions to call for this object, 
5474  * For example :
5475  * <pre><code>
5476     listeners :  { 
5477        'click' : function(e) {
5478            ..... 
5479         } ,
5480         .... 
5481     } 
5482   </code></pre>
5483  */
5484     
5485     
5486     /**
5487      * Fires the specified event with the passed parameters (minus the event name).
5488      * @param {String} eventName
5489      * @param {Object...} args Variable number of parameters are passed to handlers
5490      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5491      */
5492     fireEvent : function(){
5493         var ce = this.events[arguments[0].toLowerCase()];
5494         if(typeof ce == "object"){
5495             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5496         }else{
5497             return true;
5498         }
5499     },
5500
5501     // private
5502     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5503
5504     /**
5505      * Appends an event handler to this component
5506      * @param {String}   eventName The type of event to listen for
5507      * @param {Function} handler The method the event invokes
5508      * @param {Object}   scope (optional) The scope in which to execute the handler
5509      * function. The handler function's "this" context.
5510      * @param {Object}   options (optional) An object containing handler configuration
5511      * properties. This may contain any of the following properties:<ul>
5512      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5513      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5514      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5515      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5516      * by the specified number of milliseconds. If the event fires again within that time, the original
5517      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5518      * </ul><br>
5519      * <p>
5520      * <b>Combining Options</b><br>
5521      * Using the options argument, it is possible to combine different types of listeners:<br>
5522      * <br>
5523      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5524                 <pre><code>
5525                 el.on('click', this.onClick, this, {
5526                         single: true,
5527                 delay: 100,
5528                 forumId: 4
5529                 });
5530                 </code></pre>
5531      * <p>
5532      * <b>Attaching multiple handlers in 1 call</b><br>
5533      * The method also allows for a single argument to be passed which is a config object containing properties
5534      * which specify multiple handlers.
5535      * <pre><code>
5536                 el.on({
5537                         'click': {
5538                         fn: this.onClick,
5539                         scope: this,
5540                         delay: 100
5541                 }, 
5542                 'mouseover': {
5543                         fn: this.onMouseOver,
5544                         scope: this
5545                 },
5546                 'mouseout': {
5547                         fn: this.onMouseOut,
5548                         scope: this
5549                 }
5550                 });
5551                 </code></pre>
5552      * <p>
5553      * Or a shorthand syntax which passes the same scope object to all handlers:
5554         <pre><code>
5555                 el.on({
5556                         'click': this.onClick,
5557                 'mouseover': this.onMouseOver,
5558                 'mouseout': this.onMouseOut,
5559                 scope: this
5560                 });
5561                 </code></pre>
5562      */
5563     addListener : function(eventName, fn, scope, o){
5564         if(typeof eventName == "object"){
5565             o = eventName;
5566             for(var e in o){
5567                 if(this.filterOptRe.test(e)){
5568                     continue;
5569                 }
5570                 if(typeof o[e] == "function"){
5571                     // shared options
5572                     this.addListener(e, o[e], o.scope,  o);
5573                 }else{
5574                     // individual options
5575                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5576                 }
5577             }
5578             return;
5579         }
5580         o = (!o || typeof o == "boolean") ? {} : o;
5581         eventName = eventName.toLowerCase();
5582         var ce = this.events[eventName] || true;
5583         if(typeof ce == "boolean"){
5584             ce = new Roo.util.Event(this, eventName);
5585             this.events[eventName] = ce;
5586         }
5587         ce.addListener(fn, scope, o);
5588     },
5589
5590     /**
5591      * Removes a listener
5592      * @param {String}   eventName     The type of event to listen for
5593      * @param {Function} handler        The handler to remove
5594      * @param {Object}   scope  (optional) The scope (this object) for the handler
5595      */
5596     removeListener : function(eventName, fn, scope){
5597         var ce = this.events[eventName.toLowerCase()];
5598         if(typeof ce == "object"){
5599             ce.removeListener(fn, scope);
5600         }
5601     },
5602
5603     /**
5604      * Removes all listeners for this object
5605      */
5606     purgeListeners : function(){
5607         for(var evt in this.events){
5608             if(typeof this.events[evt] == "object"){
5609                  this.events[evt].clearListeners();
5610             }
5611         }
5612     },
5613
5614     relayEvents : function(o, events){
5615         var createHandler = function(ename){
5616             return function(){
5617                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5618             };
5619         };
5620         for(var i = 0, len = events.length; i < len; i++){
5621             var ename = events[i];
5622             if(!this.events[ename]){ this.events[ename] = true; };
5623             o.on(ename, createHandler(ename), this);
5624         }
5625     },
5626
5627     /**
5628      * Used to define events on this Observable
5629      * @param {Object} object The object with the events defined
5630      */
5631     addEvents : function(o){
5632         if(!this.events){
5633             this.events = {};
5634         }
5635         Roo.applyIf(this.events, o);
5636     },
5637
5638     /**
5639      * Checks to see if this object has any listeners for a specified event
5640      * @param {String} eventName The name of the event to check for
5641      * @return {Boolean} True if the event is being listened for, else false
5642      */
5643     hasListener : function(eventName){
5644         var e = this.events[eventName];
5645         return typeof e == "object" && e.listeners.length > 0;
5646     }
5647 };
5648 /**
5649  * Appends an event handler to this element (shorthand for addListener)
5650  * @param {String}   eventName     The type of event to listen for
5651  * @param {Function} handler        The method the event invokes
5652  * @param {Object}   scope (optional) The scope in which to execute the handler
5653  * function. The handler function's "this" context.
5654  * @param {Object}   options  (optional)
5655  * @method
5656  */
5657 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5658 /**
5659  * Removes a listener (shorthand for removeListener)
5660  * @param {String}   eventName     The type of event to listen for
5661  * @param {Function} handler        The handler to remove
5662  * @param {Object}   scope  (optional) The scope (this object) for the handler
5663  * @method
5664  */
5665 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5666
5667 /**
5668  * Starts capture on the specified Observable. All events will be passed
5669  * to the supplied function with the event name + standard signature of the event
5670  * <b>before</b> the event is fired. If the supplied function returns false,
5671  * the event will not fire.
5672  * @param {Observable} o The Observable to capture
5673  * @param {Function} fn The function to call
5674  * @param {Object} scope (optional) The scope (this object) for the fn
5675  * @static
5676  */
5677 Roo.util.Observable.capture = function(o, fn, scope){
5678     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5679 };
5680
5681 /**
5682  * Removes <b>all</b> added captures from the Observable.
5683  * @param {Observable} o The Observable to release
5684  * @static
5685  */
5686 Roo.util.Observable.releaseCapture = function(o){
5687     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5688 };
5689
5690 (function(){
5691
5692     var createBuffered = function(h, o, scope){
5693         var task = new Roo.util.DelayedTask();
5694         return function(){
5695             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5696         };
5697     };
5698
5699     var createSingle = function(h, e, fn, scope){
5700         return function(){
5701             e.removeListener(fn, scope);
5702             return h.apply(scope, arguments);
5703         };
5704     };
5705
5706     var createDelayed = function(h, o, scope){
5707         return function(){
5708             var args = Array.prototype.slice.call(arguments, 0);
5709             setTimeout(function(){
5710                 h.apply(scope, args);
5711             }, o.delay || 10);
5712         };
5713     };
5714
5715     Roo.util.Event = function(obj, name){
5716         this.name = name;
5717         this.obj = obj;
5718         this.listeners = [];
5719     };
5720
5721     Roo.util.Event.prototype = {
5722         addListener : function(fn, scope, options){
5723             var o = options || {};
5724             scope = scope || this.obj;
5725             if(!this.isListening(fn, scope)){
5726                 var l = {fn: fn, scope: scope, options: o};
5727                 var h = fn;
5728                 if(o.delay){
5729                     h = createDelayed(h, o, scope);
5730                 }
5731                 if(o.single){
5732                     h = createSingle(h, this, fn, scope);
5733                 }
5734                 if(o.buffer){
5735                     h = createBuffered(h, o, scope);
5736                 }
5737                 l.fireFn = h;
5738                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5739                     this.listeners.push(l);
5740                 }else{
5741                     this.listeners = this.listeners.slice(0);
5742                     this.listeners.push(l);
5743                 }
5744             }
5745         },
5746
5747         findListener : function(fn, scope){
5748             scope = scope || this.obj;
5749             var ls = this.listeners;
5750             for(var i = 0, len = ls.length; i < len; i++){
5751                 var l = ls[i];
5752                 if(l.fn == fn && l.scope == scope){
5753                     return i;
5754                 }
5755             }
5756             return -1;
5757         },
5758
5759         isListening : function(fn, scope){
5760             return this.findListener(fn, scope) != -1;
5761         },
5762
5763         removeListener : function(fn, scope){
5764             var index;
5765             if((index = this.findListener(fn, scope)) != -1){
5766                 if(!this.firing){
5767                     this.listeners.splice(index, 1);
5768                 }else{
5769                     this.listeners = this.listeners.slice(0);
5770                     this.listeners.splice(index, 1);
5771                 }
5772                 return true;
5773             }
5774             return false;
5775         },
5776
5777         clearListeners : function(){
5778             this.listeners = [];
5779         },
5780
5781         fire : function(){
5782             var ls = this.listeners, scope, len = ls.length;
5783             if(len > 0){
5784                 this.firing = true;
5785                 var args = Array.prototype.slice.call(arguments, 0);
5786                 for(var i = 0; i < len; i++){
5787                     var l = ls[i];
5788                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5789                         this.firing = false;
5790                         return false;
5791                     }
5792                 }
5793                 this.firing = false;
5794             }
5795             return true;
5796         }
5797     };
5798 })();/*
5799  * Based on:
5800  * Ext JS Library 1.1.1
5801  * Copyright(c) 2006-2007, Ext JS, LLC.
5802  *
5803  * Originally Released Under LGPL - original licence link has changed is not relivant.
5804  *
5805  * Fork - LGPL
5806  * <script type="text/javascript">
5807  */
5808
5809 /**
5810  * @class Roo.EventManager
5811  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5812  * several useful events directly.
5813  * See {@link Roo.EventObject} for more details on normalized event objects.
5814  * @singleton
5815  */
5816 Roo.EventManager = function(){
5817     var docReadyEvent, docReadyProcId, docReadyState = false;
5818     var resizeEvent, resizeTask, textEvent, textSize;
5819     var E = Roo.lib.Event;
5820     var D = Roo.lib.Dom;
5821
5822
5823     var fireDocReady = function(){
5824         if(!docReadyState){
5825             docReadyState = true;
5826             Roo.isReady = true;
5827             if(docReadyProcId){
5828                 clearInterval(docReadyProcId);
5829             }
5830             if(Roo.isGecko || Roo.isOpera) {
5831                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5832             }
5833             if(Roo.isIE){
5834                 var defer = document.getElementById("ie-deferred-loader");
5835                 if(defer){
5836                     defer.onreadystatechange = null;
5837                     defer.parentNode.removeChild(defer);
5838                 }
5839             }
5840             if(docReadyEvent){
5841                 docReadyEvent.fire();
5842                 docReadyEvent.clearListeners();
5843             }
5844         }
5845     };
5846     
5847     var initDocReady = function(){
5848         docReadyEvent = new Roo.util.Event();
5849         if(Roo.isGecko || Roo.isOpera) {
5850             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5851         }else if(Roo.isIE){
5852             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5853             var defer = document.getElementById("ie-deferred-loader");
5854             defer.onreadystatechange = function(){
5855                 if(this.readyState == "complete"){
5856                     fireDocReady();
5857                 }
5858             };
5859         }else if(Roo.isSafari){ 
5860             docReadyProcId = setInterval(function(){
5861                 var rs = document.readyState;
5862                 if(rs == "complete") {
5863                     fireDocReady();     
5864                  }
5865             }, 10);
5866         }
5867         // no matter what, make sure it fires on load
5868         E.on(window, "load", fireDocReady);
5869     };
5870
5871     var createBuffered = function(h, o){
5872         var task = new Roo.util.DelayedTask(h);
5873         return function(e){
5874             // create new event object impl so new events don't wipe out properties
5875             e = new Roo.EventObjectImpl(e);
5876             task.delay(o.buffer, h, null, [e]);
5877         };
5878     };
5879
5880     var createSingle = function(h, el, ename, fn){
5881         return function(e){
5882             Roo.EventManager.removeListener(el, ename, fn);
5883             h(e);
5884         };
5885     };
5886
5887     var createDelayed = function(h, o){
5888         return function(e){
5889             // create new event object impl so new events don't wipe out properties
5890             e = new Roo.EventObjectImpl(e);
5891             setTimeout(function(){
5892                 h(e);
5893             }, o.delay || 10);
5894         };
5895     };
5896
5897     var listen = function(element, ename, opt, fn, scope){
5898         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5899         fn = fn || o.fn; scope = scope || o.scope;
5900         var el = Roo.getDom(element);
5901         if(!el){
5902             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5903         }
5904         var h = function(e){
5905             e = Roo.EventObject.setEvent(e);
5906             var t;
5907             if(o.delegate){
5908                 t = e.getTarget(o.delegate, el);
5909                 if(!t){
5910                     return;
5911                 }
5912             }else{
5913                 t = e.target;
5914             }
5915             if(o.stopEvent === true){
5916                 e.stopEvent();
5917             }
5918             if(o.preventDefault === true){
5919                e.preventDefault();
5920             }
5921             if(o.stopPropagation === true){
5922                 e.stopPropagation();
5923             }
5924
5925             if(o.normalized === false){
5926                 e = e.browserEvent;
5927             }
5928
5929             fn.call(scope || el, e, t, o);
5930         };
5931         if(o.delay){
5932             h = createDelayed(h, o);
5933         }
5934         if(o.single){
5935             h = createSingle(h, el, ename, fn);
5936         }
5937         if(o.buffer){
5938             h = createBuffered(h, o);
5939         }
5940         fn._handlers = fn._handlers || [];
5941         fn._handlers.push([Roo.id(el), ename, h]);
5942
5943         E.on(el, ename, h);
5944         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5945             el.addEventListener("DOMMouseScroll", h, false);
5946             E.on(window, 'unload', function(){
5947                 el.removeEventListener("DOMMouseScroll", h, false);
5948             });
5949         }
5950         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5951             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5952         }
5953         return h;
5954     };
5955
5956     var stopListening = function(el, ename, fn){
5957         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5958         if(hds){
5959             for(var i = 0, len = hds.length; i < len; i++){
5960                 var h = hds[i];
5961                 if(h[0] == id && h[1] == ename){
5962                     hd = h[2];
5963                     hds.splice(i, 1);
5964                     break;
5965                 }
5966             }
5967         }
5968         E.un(el, ename, hd);
5969         el = Roo.getDom(el);
5970         if(ename == "mousewheel" && el.addEventListener){
5971             el.removeEventListener("DOMMouseScroll", hd, false);
5972         }
5973         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5974             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5975         }
5976     };
5977
5978     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5979     
5980     var pub = {
5981         
5982         
5983         /** 
5984          * Fix for doc tools
5985          * @scope Roo.EventManager
5986          */
5987         
5988         
5989         /** 
5990          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5991          * object with a Roo.EventObject
5992          * @param {Function} fn        The method the event invokes
5993          * @param {Object}   scope    An object that becomes the scope of the handler
5994          * @param {boolean}  override If true, the obj passed in becomes
5995          *                             the execution scope of the listener
5996          * @return {Function} The wrapped function
5997          * @deprecated
5998          */
5999         wrap : function(fn, scope, override){
6000             return function(e){
6001                 Roo.EventObject.setEvent(e);
6002                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6003             };
6004         },
6005         
6006         /**
6007      * Appends an event handler to an element (shorthand for addListener)
6008      * @param {String/HTMLElement}   element        The html element or id to assign the
6009      * @param {String}   eventName The type of event to listen for
6010      * @param {Function} handler The method the event invokes
6011      * @param {Object}   scope (optional) The scope in which to execute the handler
6012      * function. The handler function's "this" context.
6013      * @param {Object}   options (optional) An object containing handler configuration
6014      * properties. This may contain any of the following properties:<ul>
6015      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6016      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6017      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6018      * <li>preventDefault {Boolean} True to prevent the default action</li>
6019      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6020      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6021      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6022      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6023      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6024      * by the specified number of milliseconds. If the event fires again within that time, the original
6025      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6026      * </ul><br>
6027      * <p>
6028      * <b>Combining Options</b><br>
6029      * Using the options argument, it is possible to combine different types of listeners:<br>
6030      * <br>
6031      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6032      * Code:<pre><code>
6033 el.on('click', this.onClick, this, {
6034     single: true,
6035     delay: 100,
6036     stopEvent : true,
6037     forumId: 4
6038 });</code></pre>
6039      * <p>
6040      * <b>Attaching multiple handlers in 1 call</b><br>
6041       * The method also allows for a single argument to be passed which is a config object containing properties
6042      * which specify multiple handlers.
6043      * <p>
6044      * Code:<pre><code>
6045 el.on({
6046     'click' : {
6047         fn: this.onClick
6048         scope: this,
6049         delay: 100
6050     },
6051     'mouseover' : {
6052         fn: this.onMouseOver
6053         scope: this
6054     },
6055     'mouseout' : {
6056         fn: this.onMouseOut
6057         scope: this
6058     }
6059 });</code></pre>
6060      * <p>
6061      * Or a shorthand syntax:<br>
6062      * Code:<pre><code>
6063 el.on({
6064     'click' : this.onClick,
6065     'mouseover' : this.onMouseOver,
6066     'mouseout' : this.onMouseOut
6067     scope: this
6068 });</code></pre>
6069      */
6070         addListener : function(element, eventName, fn, scope, options){
6071             if(typeof eventName == "object"){
6072                 var o = eventName;
6073                 for(var e in o){
6074                     if(propRe.test(e)){
6075                         continue;
6076                     }
6077                     if(typeof o[e] == "function"){
6078                         // shared options
6079                         listen(element, e, o, o[e], o.scope);
6080                     }else{
6081                         // individual options
6082                         listen(element, e, o[e]);
6083                     }
6084                 }
6085                 return;
6086             }
6087             return listen(element, eventName, options, fn, scope);
6088         },
6089         
6090         /**
6091          * Removes an event handler
6092          *
6093          * @param {String/HTMLElement}   element        The id or html element to remove the 
6094          *                             event from
6095          * @param {String}   eventName     The type of event
6096          * @param {Function} fn
6097          * @return {Boolean} True if a listener was actually removed
6098          */
6099         removeListener : function(element, eventName, fn){
6100             return stopListening(element, eventName, fn);
6101         },
6102         
6103         /**
6104          * Fires when the document is ready (before onload and before images are loaded). Can be 
6105          * accessed shorthanded Roo.onReady().
6106          * @param {Function} fn        The method the event invokes
6107          * @param {Object}   scope    An  object that becomes the scope of the handler
6108          * @param {boolean}  options
6109          */
6110         onDocumentReady : function(fn, scope, options){
6111             if(docReadyState){ // if it already fired
6112                 docReadyEvent.addListener(fn, scope, options);
6113                 docReadyEvent.fire();
6114                 docReadyEvent.clearListeners();
6115                 return;
6116             }
6117             if(!docReadyEvent){
6118                 initDocReady();
6119             }
6120             docReadyEvent.addListener(fn, scope, options);
6121         },
6122         
6123         /**
6124          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6125          * @param {Function} fn        The method the event invokes
6126          * @param {Object}   scope    An object that becomes the scope of the handler
6127          * @param {boolean}  options
6128          */
6129         onWindowResize : function(fn, scope, options){
6130             if(!resizeEvent){
6131                 resizeEvent = new Roo.util.Event();
6132                 resizeTask = new Roo.util.DelayedTask(function(){
6133                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6134                 });
6135                 E.on(window, "resize", function(){
6136                     if(Roo.isIE){
6137                         resizeTask.delay(50);
6138                     }else{
6139                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6140                     }
6141                 });
6142             }
6143             resizeEvent.addListener(fn, scope, options);
6144         },
6145
6146         /**
6147          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  options
6151          */
6152         onTextResize : function(fn, scope, options){
6153             if(!textEvent){
6154                 textEvent = new Roo.util.Event();
6155                 var textEl = new Roo.Element(document.createElement('div'));
6156                 textEl.dom.className = 'x-text-resize';
6157                 textEl.dom.innerHTML = 'X';
6158                 textEl.appendTo(document.body);
6159                 textSize = textEl.dom.offsetHeight;
6160                 setInterval(function(){
6161                     if(textEl.dom.offsetHeight != textSize){
6162                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6163                     }
6164                 }, this.textResizeInterval);
6165             }
6166             textEvent.addListener(fn, scope, options);
6167         },
6168
6169         /**
6170          * Removes the passed window resize listener.
6171          * @param {Function} fn        The method the event invokes
6172          * @param {Object}   scope    The scope of handler
6173          */
6174         removeResizeListener : function(fn, scope){
6175             if(resizeEvent){
6176                 resizeEvent.removeListener(fn, scope);
6177             }
6178         },
6179
6180         // private
6181         fireResize : function(){
6182             if(resizeEvent){
6183                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6184             }   
6185         },
6186         /**
6187          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6188          */
6189         ieDeferSrc : false,
6190         /**
6191          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6192          */
6193         textResizeInterval : 50
6194     };
6195     
6196     /**
6197      * Fix for doc tools
6198      * @scopeAlias pub=Roo.EventManager
6199      */
6200     
6201      /**
6202      * Appends an event handler to an element (shorthand for addListener)
6203      * @param {String/HTMLElement}   element        The html element or id to assign the
6204      * @param {String}   eventName The type of event to listen for
6205      * @param {Function} handler The method the event invokes
6206      * @param {Object}   scope (optional) The scope in which to execute the handler
6207      * function. The handler function's "this" context.
6208      * @param {Object}   options (optional) An object containing handler configuration
6209      * properties. This may contain any of the following properties:<ul>
6210      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6211      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6212      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6213      * <li>preventDefault {Boolean} True to prevent the default action</li>
6214      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6215      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6216      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6217      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6218      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6219      * by the specified number of milliseconds. If the event fires again within that time, the original
6220      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6221      * </ul><br>
6222      * <p>
6223      * <b>Combining Options</b><br>
6224      * Using the options argument, it is possible to combine different types of listeners:<br>
6225      * <br>
6226      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6227      * Code:<pre><code>
6228 el.on('click', this.onClick, this, {
6229     single: true,
6230     delay: 100,
6231     stopEvent : true,
6232     forumId: 4
6233 });</code></pre>
6234      * <p>
6235      * <b>Attaching multiple handlers in 1 call</b><br>
6236       * The method also allows for a single argument to be passed which is a config object containing properties
6237      * which specify multiple handlers.
6238      * <p>
6239      * Code:<pre><code>
6240 el.on({
6241     'click' : {
6242         fn: this.onClick
6243         scope: this,
6244         delay: 100
6245     },
6246     'mouseover' : {
6247         fn: this.onMouseOver
6248         scope: this
6249     },
6250     'mouseout' : {
6251         fn: this.onMouseOut
6252         scope: this
6253     }
6254 });</code></pre>
6255      * <p>
6256      * Or a shorthand syntax:<br>
6257      * Code:<pre><code>
6258 el.on({
6259     'click' : this.onClick,
6260     'mouseover' : this.onMouseOver,
6261     'mouseout' : this.onMouseOut
6262     scope: this
6263 });</code></pre>
6264      */
6265     pub.on = pub.addListener;
6266     pub.un = pub.removeListener;
6267
6268     pub.stoppedMouseDownEvent = new Roo.util.Event();
6269     return pub;
6270 }();
6271 /**
6272   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6273   * @param {Function} fn        The method the event invokes
6274   * @param {Object}   scope    An  object that becomes the scope of the handler
6275   * @param {boolean}  override If true, the obj passed in becomes
6276   *                             the execution scope of the listener
6277   * @member Roo
6278   * @method onReady
6279  */
6280 Roo.onReady = Roo.EventManager.onDocumentReady;
6281
6282 Roo.onReady(function(){
6283     var bd = Roo.get(document.body);
6284     if(!bd){ return; }
6285
6286     var cls = [
6287             Roo.isIE ? "roo-ie"
6288             : Roo.isGecko ? "roo-gecko"
6289             : Roo.isOpera ? "roo-opera"
6290             : Roo.isSafari ? "roo-safari" : ""];
6291
6292     if(Roo.isMac){
6293         cls.push("roo-mac");
6294     }
6295     if(Roo.isLinux){
6296         cls.push("roo-linux");
6297     }
6298     if(Roo.isBorderBox){
6299         cls.push('roo-border-box');
6300     }
6301     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6302         var p = bd.dom.parentNode;
6303         if(p){
6304             p.className += ' roo-strict';
6305         }
6306     }
6307     bd.addClass(cls.join(' '));
6308 });
6309
6310 /**
6311  * @class Roo.EventObject
6312  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6313  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6314  * Example:
6315  * <pre><code>
6316  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6317     e.preventDefault();
6318     var target = e.getTarget();
6319     ...
6320  }
6321  var myDiv = Roo.get("myDiv");
6322  myDiv.on("click", handleClick);
6323  //or
6324  Roo.EventManager.on("myDiv", 'click', handleClick);
6325  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6326  </code></pre>
6327  * @singleton
6328  */
6329 Roo.EventObject = function(){
6330     
6331     var E = Roo.lib.Event;
6332     
6333     // safari keypress events for special keys return bad keycodes
6334     var safariKeys = {
6335         63234 : 37, // left
6336         63235 : 39, // right
6337         63232 : 38, // up
6338         63233 : 40, // down
6339         63276 : 33, // page up
6340         63277 : 34, // page down
6341         63272 : 46, // delete
6342         63273 : 36, // home
6343         63275 : 35  // end
6344     };
6345
6346     // normalize button clicks
6347     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6348                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6349
6350     Roo.EventObjectImpl = function(e){
6351         if(e){
6352             this.setEvent(e.browserEvent || e);
6353         }
6354     };
6355     Roo.EventObjectImpl.prototype = {
6356         /**
6357          * Used to fix doc tools.
6358          * @scope Roo.EventObject.prototype
6359          */
6360             
6361
6362         
6363         
6364         /** The normal browser event */
6365         browserEvent : null,
6366         /** The button pressed in a mouse event */
6367         button : -1,
6368         /** True if the shift key was down during the event */
6369         shiftKey : false,
6370         /** True if the control key was down during the event */
6371         ctrlKey : false,
6372         /** True if the alt key was down during the event */
6373         altKey : false,
6374
6375         /** Key constant 
6376         * @type Number */
6377         BACKSPACE : 8,
6378         /** Key constant 
6379         * @type Number */
6380         TAB : 9,
6381         /** Key constant 
6382         * @type Number */
6383         RETURN : 13,
6384         /** Key constant 
6385         * @type Number */
6386         ENTER : 13,
6387         /** Key constant 
6388         * @type Number */
6389         SHIFT : 16,
6390         /** Key constant 
6391         * @type Number */
6392         CONTROL : 17,
6393         /** Key constant 
6394         * @type Number */
6395         ESC : 27,
6396         /** Key constant 
6397         * @type Number */
6398         SPACE : 32,
6399         /** Key constant 
6400         * @type Number */
6401         PAGEUP : 33,
6402         /** Key constant 
6403         * @type Number */
6404         PAGEDOWN : 34,
6405         /** Key constant 
6406         * @type Number */
6407         END : 35,
6408         /** Key constant 
6409         * @type Number */
6410         HOME : 36,
6411         /** Key constant 
6412         * @type Number */
6413         LEFT : 37,
6414         /** Key constant 
6415         * @type Number */
6416         UP : 38,
6417         /** Key constant 
6418         * @type Number */
6419         RIGHT : 39,
6420         /** Key constant 
6421         * @type Number */
6422         DOWN : 40,
6423         /** Key constant 
6424         * @type Number */
6425         DELETE : 46,
6426         /** Key constant 
6427         * @type Number */
6428         F5 : 116,
6429
6430            /** @private */
6431         setEvent : function(e){
6432             if(e == this || (e && e.browserEvent)){ // already wrapped
6433                 return e;
6434             }
6435             this.browserEvent = e;
6436             if(e){
6437                 // normalize buttons
6438                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6439                 if(e.type == 'click' && this.button == -1){
6440                     this.button = 0;
6441                 }
6442                 this.type = e.type;
6443                 this.shiftKey = e.shiftKey;
6444                 // mac metaKey behaves like ctrlKey
6445                 this.ctrlKey = e.ctrlKey || e.metaKey;
6446                 this.altKey = e.altKey;
6447                 // in getKey these will be normalized for the mac
6448                 this.keyCode = e.keyCode;
6449                 // keyup warnings on firefox.
6450                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6451                 // cache the target for the delayed and or buffered events
6452                 this.target = E.getTarget(e);
6453                 // same for XY
6454                 this.xy = E.getXY(e);
6455             }else{
6456                 this.button = -1;
6457                 this.shiftKey = false;
6458                 this.ctrlKey = false;
6459                 this.altKey = false;
6460                 this.keyCode = 0;
6461                 this.charCode =0;
6462                 this.target = null;
6463                 this.xy = [0, 0];
6464             }
6465             return this;
6466         },
6467
6468         /**
6469          * Stop the event (preventDefault and stopPropagation)
6470          */
6471         stopEvent : function(){
6472             if(this.browserEvent){
6473                 if(this.browserEvent.type == 'mousedown'){
6474                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6475                 }
6476                 E.stopEvent(this.browserEvent);
6477             }
6478         },
6479
6480         /**
6481          * Prevents the browsers default handling of the event.
6482          */
6483         preventDefault : function(){
6484             if(this.browserEvent){
6485                 E.preventDefault(this.browserEvent);
6486             }
6487         },
6488
6489         /** @private */
6490         isNavKeyPress : function(){
6491             var k = this.keyCode;
6492             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6493             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6494         },
6495
6496         isSpecialKey : function(){
6497             var k = this.keyCode;
6498             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6499             (k == 16) || (k == 17) ||
6500             (k >= 18 && k <= 20) ||
6501             (k >= 33 && k <= 35) ||
6502             (k >= 36 && k <= 39) ||
6503             (k >= 44 && k <= 45);
6504         },
6505         /**
6506          * Cancels bubbling of the event.
6507          */
6508         stopPropagation : function(){
6509             if(this.browserEvent){
6510                 if(this.type == 'mousedown'){
6511                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6512                 }
6513                 E.stopPropagation(this.browserEvent);
6514             }
6515         },
6516
6517         /**
6518          * Gets the key code for the event.
6519          * @return {Number}
6520          */
6521         getCharCode : function(){
6522             return this.charCode || this.keyCode;
6523         },
6524
6525         /**
6526          * Returns a normalized keyCode for the event.
6527          * @return {Number} The key code
6528          */
6529         getKey : function(){
6530             var k = this.keyCode || this.charCode;
6531             return Roo.isSafari ? (safariKeys[k] || k) : k;
6532         },
6533
6534         /**
6535          * Gets the x coordinate of the event.
6536          * @return {Number}
6537          */
6538         getPageX : function(){
6539             return this.xy[0];
6540         },
6541
6542         /**
6543          * Gets the y coordinate of the event.
6544          * @return {Number}
6545          */
6546         getPageY : function(){
6547             return this.xy[1];
6548         },
6549
6550         /**
6551          * Gets the time of the event.
6552          * @return {Number}
6553          */
6554         getTime : function(){
6555             if(this.browserEvent){
6556                 return E.getTime(this.browserEvent);
6557             }
6558             return null;
6559         },
6560
6561         /**
6562          * Gets the page coordinates of the event.
6563          * @return {Array} The xy values like [x, y]
6564          */
6565         getXY : function(){
6566             return this.xy;
6567         },
6568
6569         /**
6570          * Gets the target for the event.
6571          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6572          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6573                 search as a number or element (defaults to 10 || document.body)
6574          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6575          * @return {HTMLelement}
6576          */
6577         getTarget : function(selector, maxDepth, returnEl){
6578             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6579         },
6580         /**
6581          * Gets the related target.
6582          * @return {HTMLElement}
6583          */
6584         getRelatedTarget : function(){
6585             if(this.browserEvent){
6586                 return E.getRelatedTarget(this.browserEvent);
6587             }
6588             return null;
6589         },
6590
6591         /**
6592          * Normalizes mouse wheel delta across browsers
6593          * @return {Number} The delta
6594          */
6595         getWheelDelta : function(){
6596             var e = this.browserEvent;
6597             var delta = 0;
6598             if(e.wheelDelta){ /* IE/Opera. */
6599                 delta = e.wheelDelta/120;
6600             }else if(e.detail){ /* Mozilla case. */
6601                 delta = -e.detail/3;
6602             }
6603             return delta;
6604         },
6605
6606         /**
6607          * Returns true if the control, meta, shift or alt key was pressed during this event.
6608          * @return {Boolean}
6609          */
6610         hasModifier : function(){
6611             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6612         },
6613
6614         /**
6615          * Returns true if the target of this event equals el or is a child of el
6616          * @param {String/HTMLElement/Element} el
6617          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6618          * @return {Boolean}
6619          */
6620         within : function(el, related){
6621             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6622             return t && Roo.fly(el).contains(t);
6623         },
6624
6625         getPoint : function(){
6626             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6627         }
6628     };
6629
6630     return new Roo.EventObjectImpl();
6631 }();
6632             
6633     /*
6634  * Based on:
6635  * Ext JS Library 1.1.1
6636  * Copyright(c) 2006-2007, Ext JS, LLC.
6637  *
6638  * Originally Released Under LGPL - original licence link has changed is not relivant.
6639  *
6640  * Fork - LGPL
6641  * <script type="text/javascript">
6642  */
6643
6644  
6645 // was in Composite Element!??!?!
6646  
6647 (function(){
6648     var D = Roo.lib.Dom;
6649     var E = Roo.lib.Event;
6650     var A = Roo.lib.Anim;
6651
6652     // local style camelizing for speed
6653     var propCache = {};
6654     var camelRe = /(-[a-z])/gi;
6655     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6656     var view = document.defaultView;
6657
6658 /**
6659  * @class Roo.Element
6660  * Represents an Element in the DOM.<br><br>
6661  * Usage:<br>
6662 <pre><code>
6663 var el = Roo.get("my-div");
6664
6665 // or with getEl
6666 var el = getEl("my-div");
6667
6668 // or with a DOM element
6669 var el = Roo.get(myDivElement);
6670 </code></pre>
6671  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6672  * each call instead of constructing a new one.<br><br>
6673  * <b>Animations</b><br />
6674  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6675  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6676 <pre>
6677 Option    Default   Description
6678 --------- --------  ---------------------------------------------
6679 duration  .35       The duration of the animation in seconds
6680 easing    easeOut   The YUI easing method
6681 callback  none      A function to execute when the anim completes
6682 scope     this      The scope (this) of the callback function
6683 </pre>
6684 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6685 * manipulate the animation. Here's an example:
6686 <pre><code>
6687 var el = Roo.get("my-div");
6688
6689 // no animation
6690 el.setWidth(100);
6691
6692 // default animation
6693 el.setWidth(100, true);
6694
6695 // animation with some options set
6696 el.setWidth(100, {
6697     duration: 1,
6698     callback: this.foo,
6699     scope: this
6700 });
6701
6702 // using the "anim" property to get the Anim object
6703 var opt = {
6704     duration: 1,
6705     callback: this.foo,
6706     scope: this
6707 };
6708 el.setWidth(100, opt);
6709 ...
6710 if(opt.anim.isAnimated()){
6711     opt.anim.stop();
6712 }
6713 </code></pre>
6714 * <b> Composite (Collections of) Elements</b><br />
6715  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6716  * @constructor Create a new Element directly.
6717  * @param {String/HTMLElement} element
6718  * @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).
6719  */
6720     Roo.Element = function(element, forceNew){
6721         var dom = typeof element == "string" ?
6722                 document.getElementById(element) : element;
6723         if(!dom){ // invalid id/element
6724             return null;
6725         }
6726         var id = dom.id;
6727         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6728             return Roo.Element.cache[id];
6729         }
6730
6731         /**
6732          * The DOM element
6733          * @type HTMLElement
6734          */
6735         this.dom = dom;
6736
6737         /**
6738          * The DOM element ID
6739          * @type String
6740          */
6741         this.id = id || Roo.id(dom);
6742     };
6743
6744     var El = Roo.Element;
6745
6746     El.prototype = {
6747         /**
6748          * The element's default display mode  (defaults to "")
6749          * @type String
6750          */
6751         originalDisplay : "",
6752
6753         visibilityMode : 1,
6754         /**
6755          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6756          * @type String
6757          */
6758         defaultUnit : "px",
6759         /**
6760          * Sets the element's visibility mode. When setVisible() is called it
6761          * will use this to determine whether to set the visibility or the display property.
6762          * @param visMode Element.VISIBILITY or Element.DISPLAY
6763          * @return {Roo.Element} this
6764          */
6765         setVisibilityMode : function(visMode){
6766             this.visibilityMode = visMode;
6767             return this;
6768         },
6769         /**
6770          * Convenience method for setVisibilityMode(Element.DISPLAY)
6771          * @param {String} display (optional) What to set display to when visible
6772          * @return {Roo.Element} this
6773          */
6774         enableDisplayMode : function(display){
6775             this.setVisibilityMode(El.DISPLAY);
6776             if(typeof display != "undefined") this.originalDisplay = display;
6777             return this;
6778         },
6779
6780         /**
6781          * 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)
6782          * @param {String} selector The simple selector to test
6783          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6784                 search as a number or element (defaults to 10 || document.body)
6785          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6786          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6787          */
6788         findParent : function(simpleSelector, maxDepth, returnEl){
6789             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6790             maxDepth = maxDepth || 50;
6791             if(typeof maxDepth != "number"){
6792                 stopEl = Roo.getDom(maxDepth);
6793                 maxDepth = 10;
6794             }
6795             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6796                 if(dq.is(p, simpleSelector)){
6797                     return returnEl ? Roo.get(p) : p;
6798                 }
6799                 depth++;
6800                 p = p.parentNode;
6801             }
6802             return null;
6803         },
6804
6805
6806         /**
6807          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6808          * @param {String} selector The simple selector to test
6809          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6810                 search as a number or element (defaults to 10 || document.body)
6811          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6812          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6813          */
6814         findParentNode : function(simpleSelector, maxDepth, returnEl){
6815             var p = Roo.fly(this.dom.parentNode, '_internal');
6816             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6817         },
6818
6819         /**
6820          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6821          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6822          * @param {String} selector The simple selector to test
6823          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6824                 search as a number or element (defaults to 10 || document.body)
6825          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6826          */
6827         up : function(simpleSelector, maxDepth){
6828             return this.findParentNode(simpleSelector, maxDepth, true);
6829         },
6830
6831
6832
6833         /**
6834          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6835          * @param {String} selector The simple selector to test
6836          * @return {Boolean} True if this element matches the selector, else false
6837          */
6838         is : function(simpleSelector){
6839             return Roo.DomQuery.is(this.dom, simpleSelector);
6840         },
6841
6842         /**
6843          * Perform animation on this element.
6844          * @param {Object} args The YUI animation control args
6845          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6846          * @param {Function} onComplete (optional) Function to call when animation completes
6847          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6848          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6849          * @return {Roo.Element} this
6850          */
6851         animate : function(args, duration, onComplete, easing, animType){
6852             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6853             return this;
6854         },
6855
6856         /*
6857          * @private Internal animation call
6858          */
6859         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6860             animType = animType || 'run';
6861             opt = opt || {};
6862             var anim = Roo.lib.Anim[animType](
6863                 this.dom, args,
6864                 (opt.duration || defaultDur) || .35,
6865                 (opt.easing || defaultEase) || 'easeOut',
6866                 function(){
6867                     Roo.callback(cb, this);
6868                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6869                 },
6870                 this
6871             );
6872             opt.anim = anim;
6873             return anim;
6874         },
6875
6876         // private legacy anim prep
6877         preanim : function(a, i){
6878             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6879         },
6880
6881         /**
6882          * Removes worthless text nodes
6883          * @param {Boolean} forceReclean (optional) By default the element
6884          * keeps track if it has been cleaned already so
6885          * you can call this over and over. However, if you update the element and
6886          * need to force a reclean, you can pass true.
6887          */
6888         clean : function(forceReclean){
6889             if(this.isCleaned && forceReclean !== true){
6890                 return this;
6891             }
6892             var ns = /\S/;
6893             var d = this.dom, n = d.firstChild, ni = -1;
6894             while(n){
6895                 var nx = n.nextSibling;
6896                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6897                     d.removeChild(n);
6898                 }else{
6899                     n.nodeIndex = ++ni;
6900                 }
6901                 n = nx;
6902             }
6903             this.isCleaned = true;
6904             return this;
6905         },
6906
6907         // private
6908         calcOffsetsTo : function(el){
6909             el = Roo.get(el);
6910             var d = el.dom;
6911             var restorePos = false;
6912             if(el.getStyle('position') == 'static'){
6913                 el.position('relative');
6914                 restorePos = true;
6915             }
6916             var x = 0, y =0;
6917             var op = this.dom;
6918             while(op && op != d && op.tagName != 'HTML'){
6919                 x+= op.offsetLeft;
6920                 y+= op.offsetTop;
6921                 op = op.offsetParent;
6922             }
6923             if(restorePos){
6924                 el.position('static');
6925             }
6926             return [x, y];
6927         },
6928
6929         /**
6930          * Scrolls this element into view within the passed container.
6931          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6932          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6933          * @return {Roo.Element} this
6934          */
6935         scrollIntoView : function(container, hscroll){
6936             var c = Roo.getDom(container) || document.body;
6937             var el = this.dom;
6938
6939             var o = this.calcOffsetsTo(c),
6940                 l = o[0],
6941                 t = o[1],
6942                 b = t+el.offsetHeight,
6943                 r = l+el.offsetWidth;
6944
6945             var ch = c.clientHeight;
6946             var ct = parseInt(c.scrollTop, 10);
6947             var cl = parseInt(c.scrollLeft, 10);
6948             var cb = ct + ch;
6949             var cr = cl + c.clientWidth;
6950
6951             if(t < ct){
6952                 c.scrollTop = t;
6953             }else if(b > cb){
6954                 c.scrollTop = b-ch;
6955             }
6956
6957             if(hscroll !== false){
6958                 if(l < cl){
6959                     c.scrollLeft = l;
6960                 }else if(r > cr){
6961                     c.scrollLeft = r-c.clientWidth;
6962                 }
6963             }
6964             return this;
6965         },
6966
6967         // private
6968         scrollChildIntoView : function(child, hscroll){
6969             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6970         },
6971
6972         /**
6973          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6974          * the new height may not be available immediately.
6975          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6976          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6977          * @param {Function} onComplete (optional) Function to call when animation completes
6978          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6979          * @return {Roo.Element} this
6980          */
6981         autoHeight : function(animate, duration, onComplete, easing){
6982             var oldHeight = this.getHeight();
6983             this.clip();
6984             this.setHeight(1); // force clipping
6985             setTimeout(function(){
6986                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6987                 if(!animate){
6988                     this.setHeight(height);
6989                     this.unclip();
6990                     if(typeof onComplete == "function"){
6991                         onComplete();
6992                     }
6993                 }else{
6994                     this.setHeight(oldHeight); // restore original height
6995                     this.setHeight(height, animate, duration, function(){
6996                         this.unclip();
6997                         if(typeof onComplete == "function") onComplete();
6998                     }.createDelegate(this), easing);
6999                 }
7000             }.createDelegate(this), 0);
7001             return this;
7002         },
7003
7004         /**
7005          * Returns true if this element is an ancestor of the passed element
7006          * @param {HTMLElement/String} el The element to check
7007          * @return {Boolean} True if this element is an ancestor of el, else false
7008          */
7009         contains : function(el){
7010             if(!el){return false;}
7011             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7012         },
7013
7014         /**
7015          * Checks whether the element is currently visible using both visibility and display properties.
7016          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7017          * @return {Boolean} True if the element is currently visible, else false
7018          */
7019         isVisible : function(deep) {
7020             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7021             if(deep !== true || !vis){
7022                 return vis;
7023             }
7024             var p = this.dom.parentNode;
7025             while(p && p.tagName.toLowerCase() != "body"){
7026                 if(!Roo.fly(p, '_isVisible').isVisible()){
7027                     return false;
7028                 }
7029                 p = p.parentNode;
7030             }
7031             return true;
7032         },
7033
7034         /**
7035          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7036          * @param {String} selector The CSS selector
7037          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7038          * @return {CompositeElement/CompositeElementLite} The composite element
7039          */
7040         select : function(selector, unique){
7041             return El.select(selector, unique, this.dom);
7042         },
7043
7044         /**
7045          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7046          * @param {String} selector The CSS selector
7047          * @return {Array} An array of the matched nodes
7048          */
7049         query : function(selector, unique){
7050             return Roo.DomQuery.select(selector, this.dom);
7051         },
7052
7053         /**
7054          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7055          * @param {String} selector The CSS selector
7056          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7057          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7058          */
7059         child : function(selector, returnDom){
7060             var n = Roo.DomQuery.selectNode(selector, this.dom);
7061             return returnDom ? n : Roo.get(n);
7062         },
7063
7064         /**
7065          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7066          * @param {String} selector The CSS selector
7067          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7068          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7069          */
7070         down : function(selector, returnDom){
7071             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7072             return returnDom ? n : Roo.get(n);
7073         },
7074
7075         /**
7076          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7077          * @param {String} group The group the DD object is member of
7078          * @param {Object} config The DD config object
7079          * @param {Object} overrides An object containing methods to override/implement on the DD object
7080          * @return {Roo.dd.DD} The DD object
7081          */
7082         initDD : function(group, config, overrides){
7083             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7084             return Roo.apply(dd, overrides);
7085         },
7086
7087         /**
7088          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7089          * @param {String} group The group the DDProxy object is member of
7090          * @param {Object} config The DDProxy config object
7091          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7092          * @return {Roo.dd.DDProxy} The DDProxy object
7093          */
7094         initDDProxy : function(group, config, overrides){
7095             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7096             return Roo.apply(dd, overrides);
7097         },
7098
7099         /**
7100          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7101          * @param {String} group The group the DDTarget object is member of
7102          * @param {Object} config The DDTarget config object
7103          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7104          * @return {Roo.dd.DDTarget} The DDTarget object
7105          */
7106         initDDTarget : function(group, config, overrides){
7107             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7108             return Roo.apply(dd, overrides);
7109         },
7110
7111         /**
7112          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7113          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7114          * @param {Boolean} visible Whether the element is visible
7115          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7116          * @return {Roo.Element} this
7117          */
7118          setVisible : function(visible, animate){
7119             if(!animate || !A){
7120                 if(this.visibilityMode == El.DISPLAY){
7121                     this.setDisplayed(visible);
7122                 }else{
7123                     this.fixDisplay();
7124                     this.dom.style.visibility = visible ? "visible" : "hidden";
7125                 }
7126             }else{
7127                 // closure for composites
7128                 var dom = this.dom;
7129                 var visMode = this.visibilityMode;
7130                 if(visible){
7131                     this.setOpacity(.01);
7132                     this.setVisible(true);
7133                 }
7134                 this.anim({opacity: { to: (visible?1:0) }},
7135                       this.preanim(arguments, 1),
7136                       null, .35, 'easeIn', function(){
7137                          if(!visible){
7138                              if(visMode == El.DISPLAY){
7139                                  dom.style.display = "none";
7140                              }else{
7141                                  dom.style.visibility = "hidden";
7142                              }
7143                              Roo.get(dom).setOpacity(1);
7144                          }
7145                      });
7146             }
7147             return this;
7148         },
7149
7150         /**
7151          * Returns true if display is not "none"
7152          * @return {Boolean}
7153          */
7154         isDisplayed : function() {
7155             return this.getStyle("display") != "none";
7156         },
7157
7158         /**
7159          * Toggles the element's visibility or display, depending on visibility mode.
7160          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7161          * @return {Roo.Element} this
7162          */
7163         toggle : function(animate){
7164             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7165             return this;
7166         },
7167
7168         /**
7169          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7170          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7171          * @return {Roo.Element} this
7172          */
7173         setDisplayed : function(value) {
7174             if(typeof value == "boolean"){
7175                value = value ? this.originalDisplay : "none";
7176             }
7177             this.setStyle("display", value);
7178             return this;
7179         },
7180
7181         /**
7182          * Tries to focus the element. Any exceptions are caught and ignored.
7183          * @return {Roo.Element} this
7184          */
7185         focus : function() {
7186             try{
7187                 this.dom.focus();
7188             }catch(e){}
7189             return this;
7190         },
7191
7192         /**
7193          * Tries to blur the element. Any exceptions are caught and ignored.
7194          * @return {Roo.Element} this
7195          */
7196         blur : function() {
7197             try{
7198                 this.dom.blur();
7199             }catch(e){}
7200             return this;
7201         },
7202
7203         /**
7204          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7205          * @param {String/Array} className The CSS class to add, or an array of classes
7206          * @return {Roo.Element} this
7207          */
7208         addClass : function(className){
7209             if(className instanceof Array){
7210                 for(var i = 0, len = className.length; i < len; i++) {
7211                     this.addClass(className[i]);
7212                 }
7213             }else{
7214                 if(className && !this.hasClass(className)){
7215                     this.dom.className = this.dom.className + " " + className;
7216                 }
7217             }
7218             return this;
7219         },
7220
7221         /**
7222          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7223          * @param {String/Array} className The CSS class to add, or an array of classes
7224          * @return {Roo.Element} this
7225          */
7226         radioClass : function(className){
7227             var siblings = this.dom.parentNode.childNodes;
7228             for(var i = 0; i < siblings.length; i++) {
7229                 var s = siblings[i];
7230                 if(s.nodeType == 1){
7231                     Roo.get(s).removeClass(className);
7232                 }
7233             }
7234             this.addClass(className);
7235             return this;
7236         },
7237
7238         /**
7239          * Removes one or more CSS classes from the element.
7240          * @param {String/Array} className The CSS class to remove, or an array of classes
7241          * @return {Roo.Element} this
7242          */
7243         removeClass : function(className){
7244             if(!className || !this.dom.className){
7245                 return this;
7246             }
7247             if(className instanceof Array){
7248                 for(var i = 0, len = className.length; i < len; i++) {
7249                     this.removeClass(className[i]);
7250                 }
7251             }else{
7252                 if(this.hasClass(className)){
7253                     var re = this.classReCache[className];
7254                     if (!re) {
7255                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7256                        this.classReCache[className] = re;
7257                     }
7258                     this.dom.className =
7259                         this.dom.className.replace(re, " ");
7260                 }
7261             }
7262             return this;
7263         },
7264
7265         // private
7266         classReCache: {},
7267
7268         /**
7269          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7270          * @param {String} className The CSS class to toggle
7271          * @return {Roo.Element} this
7272          */
7273         toggleClass : function(className){
7274             if(this.hasClass(className)){
7275                 this.removeClass(className);
7276             }else{
7277                 this.addClass(className);
7278             }
7279             return this;
7280         },
7281
7282         /**
7283          * Checks if the specified CSS class exists on this element's DOM node.
7284          * @param {String} className The CSS class to check for
7285          * @return {Boolean} True if the class exists, else false
7286          */
7287         hasClass : function(className){
7288             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7289         },
7290
7291         /**
7292          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7293          * @param {String} oldClassName The CSS class to replace
7294          * @param {String} newClassName The replacement CSS class
7295          * @return {Roo.Element} this
7296          */
7297         replaceClass : function(oldClassName, newClassName){
7298             this.removeClass(oldClassName);
7299             this.addClass(newClassName);
7300             return this;
7301         },
7302
7303         /**
7304          * Returns an object with properties matching the styles requested.
7305          * For example, el.getStyles('color', 'font-size', 'width') might return
7306          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7307          * @param {String} style1 A style name
7308          * @param {String} style2 A style name
7309          * @param {String} etc.
7310          * @return {Object} The style object
7311          */
7312         getStyles : function(){
7313             var a = arguments, len = a.length, r = {};
7314             for(var i = 0; i < len; i++){
7315                 r[a[i]] = this.getStyle(a[i]);
7316             }
7317             return r;
7318         },
7319
7320         /**
7321          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7322          * @param {String} property The style property whose value is returned.
7323          * @return {String} The current value of the style property for this element.
7324          */
7325         getStyle : function(){
7326             return view && view.getComputedStyle ?
7327                 function(prop){
7328                     var el = this.dom, v, cs, camel;
7329                     if(prop == 'float'){
7330                         prop = "cssFloat";
7331                     }
7332                     if(el.style && (v = el.style[prop])){
7333                         return v;
7334                     }
7335                     if(cs = view.getComputedStyle(el, "")){
7336                         if(!(camel = propCache[prop])){
7337                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7338                         }
7339                         return cs[camel];
7340                     }
7341                     return null;
7342                 } :
7343                 function(prop){
7344                     var el = this.dom, v, cs, camel;
7345                     if(prop == 'opacity'){
7346                         if(typeof el.style.filter == 'string'){
7347                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7348                             if(m){
7349                                 var fv = parseFloat(m[1]);
7350                                 if(!isNaN(fv)){
7351                                     return fv ? fv / 100 : 0;
7352                                 }
7353                             }
7354                         }
7355                         return 1;
7356                     }else if(prop == 'float'){
7357                         prop = "styleFloat";
7358                     }
7359                     if(!(camel = propCache[prop])){
7360                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7361                     }
7362                     if(v = el.style[camel]){
7363                         return v;
7364                     }
7365                     if(cs = el.currentStyle){
7366                         return cs[camel];
7367                     }
7368                     return null;
7369                 };
7370         }(),
7371
7372         /**
7373          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7374          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7375          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7376          * @return {Roo.Element} this
7377          */
7378         setStyle : function(prop, value){
7379             if(typeof prop == "string"){
7380                 var camel;
7381                 if(!(camel = propCache[prop])){
7382                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7383                 }
7384                 if(camel == 'opacity') {
7385                     this.setOpacity(value);
7386                 }else{
7387                     this.dom.style[camel] = value;
7388                 }
7389             }else{
7390                 for(var style in prop){
7391                     if(typeof prop[style] != "function"){
7392                        this.setStyle(style, prop[style]);
7393                     }
7394                 }
7395             }
7396             return this;
7397         },
7398
7399         /**
7400          * More flexible version of {@link #setStyle} for setting style properties.
7401          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7402          * a function which returns such a specification.
7403          * @return {Roo.Element} this
7404          */
7405         applyStyles : function(style){
7406             Roo.DomHelper.applyStyles(this.dom, style);
7407             return this;
7408         },
7409
7410         /**
7411           * 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).
7412           * @return {Number} The X position of the element
7413           */
7414         getX : function(){
7415             return D.getX(this.dom);
7416         },
7417
7418         /**
7419           * 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).
7420           * @return {Number} The Y position of the element
7421           */
7422         getY : function(){
7423             return D.getY(this.dom);
7424         },
7425
7426         /**
7427           * 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).
7428           * @return {Array} The XY position of the element
7429           */
7430         getXY : function(){
7431             return D.getXY(this.dom);
7432         },
7433
7434         /**
7435          * 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).
7436          * @param {Number} The X position of the element
7437          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7438          * @return {Roo.Element} this
7439          */
7440         setX : function(x, animate){
7441             if(!animate || !A){
7442                 D.setX(this.dom, x);
7443             }else{
7444                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7445             }
7446             return this;
7447         },
7448
7449         /**
7450          * 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).
7451          * @param {Number} The Y position of the element
7452          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7453          * @return {Roo.Element} this
7454          */
7455         setY : function(y, animate){
7456             if(!animate || !A){
7457                 D.setY(this.dom, y);
7458             }else{
7459                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7460             }
7461             return this;
7462         },
7463
7464         /**
7465          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7466          * @param {String} left The left CSS property value
7467          * @return {Roo.Element} this
7468          */
7469         setLeft : function(left){
7470             this.setStyle("left", this.addUnits(left));
7471             return this;
7472         },
7473
7474         /**
7475          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7476          * @param {String} top The top CSS property value
7477          * @return {Roo.Element} this
7478          */
7479         setTop : function(top){
7480             this.setStyle("top", this.addUnits(top));
7481             return this;
7482         },
7483
7484         /**
7485          * Sets the element's CSS right style.
7486          * @param {String} right The right CSS property value
7487          * @return {Roo.Element} this
7488          */
7489         setRight : function(right){
7490             this.setStyle("right", this.addUnits(right));
7491             return this;
7492         },
7493
7494         /**
7495          * Sets the element's CSS bottom style.
7496          * @param {String} bottom The bottom CSS property value
7497          * @return {Roo.Element} this
7498          */
7499         setBottom : function(bottom){
7500             this.setStyle("bottom", this.addUnits(bottom));
7501             return this;
7502         },
7503
7504         /**
7505          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7506          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7507          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7508          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7509          * @return {Roo.Element} this
7510          */
7511         setXY : function(pos, animate){
7512             if(!animate || !A){
7513                 D.setXY(this.dom, pos);
7514             }else{
7515                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7516             }
7517             return this;
7518         },
7519
7520         /**
7521          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7522          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7523          * @param {Number} x X value for new position (coordinates are page-based)
7524          * @param {Number} y Y value for new position (coordinates are page-based)
7525          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7526          * @return {Roo.Element} this
7527          */
7528         setLocation : function(x, y, animate){
7529             this.setXY([x, y], this.preanim(arguments, 2));
7530             return this;
7531         },
7532
7533         /**
7534          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7535          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7536          * @param {Number} x X value for new position (coordinates are page-based)
7537          * @param {Number} y Y value for new position (coordinates are page-based)
7538          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7539          * @return {Roo.Element} this
7540          */
7541         moveTo : function(x, y, animate){
7542             this.setXY([x, y], this.preanim(arguments, 2));
7543             return this;
7544         },
7545
7546         /**
7547          * Returns the region of the given element.
7548          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7549          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7550          */
7551         getRegion : function(){
7552             return D.getRegion(this.dom);
7553         },
7554
7555         /**
7556          * Returns the offset height of the element
7557          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7558          * @return {Number} The element's height
7559          */
7560         getHeight : function(contentHeight){
7561             var h = this.dom.offsetHeight || 0;
7562             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7563         },
7564
7565         /**
7566          * Returns the offset width of the element
7567          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7568          * @return {Number} The element's width
7569          */
7570         getWidth : function(contentWidth){
7571             var w = this.dom.offsetWidth || 0;
7572             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7573         },
7574
7575         /**
7576          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7577          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7578          * if a height has not been set using CSS.
7579          * @return {Number}
7580          */
7581         getComputedHeight : function(){
7582             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7583             if(!h){
7584                 h = parseInt(this.getStyle('height'), 10) || 0;
7585                 if(!this.isBorderBox()){
7586                     h += this.getFrameWidth('tb');
7587                 }
7588             }
7589             return h;
7590         },
7591
7592         /**
7593          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7594          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7595          * if a width has not been set using CSS.
7596          * @return {Number}
7597          */
7598         getComputedWidth : function(){
7599             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7600             if(!w){
7601                 w = parseInt(this.getStyle('width'), 10) || 0;
7602                 if(!this.isBorderBox()){
7603                     w += this.getFrameWidth('lr');
7604                 }
7605             }
7606             return w;
7607         },
7608
7609         /**
7610          * Returns the size of the element.
7611          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7612          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7613          */
7614         getSize : function(contentSize){
7615             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7616         },
7617
7618         /**
7619          * Returns the width and height of the viewport.
7620          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7621          */
7622         getViewSize : function(){
7623             var d = this.dom, doc = document, aw = 0, ah = 0;
7624             if(d == doc || d == doc.body){
7625                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7626             }else{
7627                 return {
7628                     width : d.clientWidth,
7629                     height: d.clientHeight
7630                 };
7631             }
7632         },
7633
7634         /**
7635          * Returns the value of the "value" attribute
7636          * @param {Boolean} asNumber true to parse the value as a number
7637          * @return {String/Number}
7638          */
7639         getValue : function(asNumber){
7640             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7641         },
7642
7643         // private
7644         adjustWidth : function(width){
7645             if(typeof width == "number"){
7646                 if(this.autoBoxAdjust && !this.isBorderBox()){
7647                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7648                 }
7649                 if(width < 0){
7650                     width = 0;
7651                 }
7652             }
7653             return width;
7654         },
7655
7656         // private
7657         adjustHeight : function(height){
7658             if(typeof height == "number"){
7659                if(this.autoBoxAdjust && !this.isBorderBox()){
7660                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7661                }
7662                if(height < 0){
7663                    height = 0;
7664                }
7665             }
7666             return height;
7667         },
7668
7669         /**
7670          * Set the width of the element
7671          * @param {Number} width The new width
7672          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7673          * @return {Roo.Element} this
7674          */
7675         setWidth : function(width, animate){
7676             width = this.adjustWidth(width);
7677             if(!animate || !A){
7678                 this.dom.style.width = this.addUnits(width);
7679             }else{
7680                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Set the height of the element
7687          * @param {Number} height The new height
7688          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691          setHeight : function(height, animate){
7692             height = this.adjustHeight(height);
7693             if(!animate || !A){
7694                 this.dom.style.height = this.addUnits(height);
7695             }else{
7696                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7697             }
7698             return this;
7699         },
7700
7701         /**
7702          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7703          * @param {Number} width The new width
7704          * @param {Number} height The new height
7705          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7706          * @return {Roo.Element} this
7707          */
7708          setSize : function(width, height, animate){
7709             if(typeof width == "object"){ // in case of object from getSize()
7710                 height = width.height; width = width.width;
7711             }
7712             width = this.adjustWidth(width); height = this.adjustHeight(height);
7713             if(!animate || !A){
7714                 this.dom.style.width = this.addUnits(width);
7715                 this.dom.style.height = this.addUnits(height);
7716             }else{
7717                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7718             }
7719             return this;
7720         },
7721
7722         /**
7723          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7724          * @param {Number} x X value for new position (coordinates are page-based)
7725          * @param {Number} y Y value for new position (coordinates are page-based)
7726          * @param {Number} width The new width
7727          * @param {Number} height The new height
7728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7729          * @return {Roo.Element} this
7730          */
7731         setBounds : function(x, y, width, height, animate){
7732             if(!animate || !A){
7733                 this.setSize(width, height);
7734                 this.setLocation(x, y);
7735             }else{
7736                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7737                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7738                               this.preanim(arguments, 4), 'motion');
7739             }
7740             return this;
7741         },
7742
7743         /**
7744          * 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.
7745          * @param {Roo.lib.Region} region The region to fill
7746          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setRegion : function(region, animate){
7750             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7751             return this;
7752         },
7753
7754         /**
7755          * Appends an event handler
7756          *
7757          * @param {String}   eventName     The type of event to append
7758          * @param {Function} fn        The method the event invokes
7759          * @param {Object} scope       (optional) The scope (this object) of the fn
7760          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7761          */
7762         addListener : function(eventName, fn, scope, options){
7763             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7764         },
7765
7766         /**
7767          * Removes an event handler from this element
7768          * @param {String} eventName the type of event to remove
7769          * @param {Function} fn the method the event invokes
7770          * @return {Roo.Element} this
7771          */
7772         removeListener : function(eventName, fn){
7773             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7774             return this;
7775         },
7776
7777         /**
7778          * Removes all previous added listeners from this element
7779          * @return {Roo.Element} this
7780          */
7781         removeAllListeners : function(){
7782             E.purgeElement(this.dom);
7783             return this;
7784         },
7785
7786         relayEvent : function(eventName, observable){
7787             this.on(eventName, function(e){
7788                 observable.fireEvent(eventName, e);
7789             });
7790         },
7791
7792         /**
7793          * Set the opacity of the element
7794          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7795          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7796          * @return {Roo.Element} this
7797          */
7798          setOpacity : function(opacity, animate){
7799             if(!animate || !A){
7800                 var s = this.dom.style;
7801                 if(Roo.isIE){
7802                     s.zoom = 1;
7803                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7804                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7805                 }else{
7806                     s.opacity = opacity;
7807                 }
7808             }else{
7809                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7810             }
7811             return this;
7812         },
7813
7814         /**
7815          * Gets the left X coordinate
7816          * @param {Boolean} local True to get the local css position instead of page coordinate
7817          * @return {Number}
7818          */
7819         getLeft : function(local){
7820             if(!local){
7821                 return this.getX();
7822             }else{
7823                 return parseInt(this.getStyle("left"), 10) || 0;
7824             }
7825         },
7826
7827         /**
7828          * Gets the right X coordinate of the element (element X position + element width)
7829          * @param {Boolean} local True to get the local css position instead of page coordinate
7830          * @return {Number}
7831          */
7832         getRight : function(local){
7833             if(!local){
7834                 return this.getX() + this.getWidth();
7835             }else{
7836                 return (this.getLeft(true) + this.getWidth()) || 0;
7837             }
7838         },
7839
7840         /**
7841          * Gets the top Y coordinate
7842          * @param {Boolean} local True to get the local css position instead of page coordinate
7843          * @return {Number}
7844          */
7845         getTop : function(local) {
7846             if(!local){
7847                 return this.getY();
7848             }else{
7849                 return parseInt(this.getStyle("top"), 10) || 0;
7850             }
7851         },
7852
7853         /**
7854          * Gets the bottom Y coordinate of the element (element Y position + element height)
7855          * @param {Boolean} local True to get the local css position instead of page coordinate
7856          * @return {Number}
7857          */
7858         getBottom : function(local){
7859             if(!local){
7860                 return this.getY() + this.getHeight();
7861             }else{
7862                 return (this.getTop(true) + this.getHeight()) || 0;
7863             }
7864         },
7865
7866         /**
7867         * Initializes positioning on this element. If a desired position is not passed, it will make the
7868         * the element positioned relative IF it is not already positioned.
7869         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7870         * @param {Number} zIndex (optional) The zIndex to apply
7871         * @param {Number} x (optional) Set the page X position
7872         * @param {Number} y (optional) Set the page Y position
7873         */
7874         position : function(pos, zIndex, x, y){
7875             if(!pos){
7876                if(this.getStyle('position') == 'static'){
7877                    this.setStyle('position', 'relative');
7878                }
7879             }else{
7880                 this.setStyle("position", pos);
7881             }
7882             if(zIndex){
7883                 this.setStyle("z-index", zIndex);
7884             }
7885             if(x !== undefined && y !== undefined){
7886                 this.setXY([x, y]);
7887             }else if(x !== undefined){
7888                 this.setX(x);
7889             }else if(y !== undefined){
7890                 this.setY(y);
7891             }
7892         },
7893
7894         /**
7895         * Clear positioning back to the default when the document was loaded
7896         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7897         * @return {Roo.Element} this
7898          */
7899         clearPositioning : function(value){
7900             value = value ||'';
7901             this.setStyle({
7902                 "left": value,
7903                 "right": value,
7904                 "top": value,
7905                 "bottom": value,
7906                 "z-index": "",
7907                 "position" : "static"
7908             });
7909             return this;
7910         },
7911
7912         /**
7913         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7914         * snapshot before performing an update and then restoring the element.
7915         * @return {Object}
7916         */
7917         getPositioning : function(){
7918             var l = this.getStyle("left");
7919             var t = this.getStyle("top");
7920             return {
7921                 "position" : this.getStyle("position"),
7922                 "left" : l,
7923                 "right" : l ? "" : this.getStyle("right"),
7924                 "top" : t,
7925                 "bottom" : t ? "" : this.getStyle("bottom"),
7926                 "z-index" : this.getStyle("z-index")
7927             };
7928         },
7929
7930         /**
7931          * Gets the width of the border(s) for the specified side(s)
7932          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7933          * passing lr would get the border (l)eft width + the border (r)ight width.
7934          * @return {Number} The width of the sides passed added together
7935          */
7936         getBorderWidth : function(side){
7937             return this.addStyles(side, El.borders);
7938         },
7939
7940         /**
7941          * Gets the width of the padding(s) for the specified side(s)
7942          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7943          * passing lr would get the padding (l)eft + the padding (r)ight.
7944          * @return {Number} The padding of the sides passed added together
7945          */
7946         getPadding : function(side){
7947             return this.addStyles(side, El.paddings);
7948         },
7949
7950         /**
7951         * Set positioning with an object returned by getPositioning().
7952         * @param {Object} posCfg
7953         * @return {Roo.Element} this
7954          */
7955         setPositioning : function(pc){
7956             this.applyStyles(pc);
7957             if(pc.right == "auto"){
7958                 this.dom.style.right = "";
7959             }
7960             if(pc.bottom == "auto"){
7961                 this.dom.style.bottom = "";
7962             }
7963             return this;
7964         },
7965
7966         // private
7967         fixDisplay : function(){
7968             if(this.getStyle("display") == "none"){
7969                 this.setStyle("visibility", "hidden");
7970                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7971                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7972                     this.setStyle("display", "block");
7973                 }
7974             }
7975         },
7976
7977         /**
7978          * Quick set left and top adding default units
7979          * @param {String} left The left CSS property value
7980          * @param {String} top The top CSS property value
7981          * @return {Roo.Element} this
7982          */
7983          setLeftTop : function(left, top){
7984             this.dom.style.left = this.addUnits(left);
7985             this.dom.style.top = this.addUnits(top);
7986             return this;
7987         },
7988
7989         /**
7990          * Move this element relative to its current position.
7991          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7992          * @param {Number} distance How far to move the element in pixels
7993          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7994          * @return {Roo.Element} this
7995          */
7996          move : function(direction, distance, animate){
7997             var xy = this.getXY();
7998             direction = direction.toLowerCase();
7999             switch(direction){
8000                 case "l":
8001                 case "left":
8002                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8003                     break;
8004                case "r":
8005                case "right":
8006                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8007                     break;
8008                case "t":
8009                case "top":
8010                case "up":
8011                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8012                     break;
8013                case "b":
8014                case "bottom":
8015                case "down":
8016                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8017                     break;
8018             }
8019             return this;
8020         },
8021
8022         /**
8023          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8024          * @return {Roo.Element} this
8025          */
8026         clip : function(){
8027             if(!this.isClipped){
8028                this.isClipped = true;
8029                this.originalClip = {
8030                    "o": this.getStyle("overflow"),
8031                    "x": this.getStyle("overflow-x"),
8032                    "y": this.getStyle("overflow-y")
8033                };
8034                this.setStyle("overflow", "hidden");
8035                this.setStyle("overflow-x", "hidden");
8036                this.setStyle("overflow-y", "hidden");
8037             }
8038             return this;
8039         },
8040
8041         /**
8042          *  Return clipping (overflow) to original clipping before clip() was called
8043          * @return {Roo.Element} this
8044          */
8045         unclip : function(){
8046             if(this.isClipped){
8047                 this.isClipped = false;
8048                 var o = this.originalClip;
8049                 if(o.o){this.setStyle("overflow", o.o);}
8050                 if(o.x){this.setStyle("overflow-x", o.x);}
8051                 if(o.y){this.setStyle("overflow-y", o.y);}
8052             }
8053             return this;
8054         },
8055
8056
8057         /**
8058          * Gets the x,y coordinates specified by the anchor position on the element.
8059          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8060          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8061          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8062          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8063          * @return {Array} [x, y] An array containing the element's x and y coordinates
8064          */
8065         getAnchorXY : function(anchor, local, s){
8066             //Passing a different size is useful for pre-calculating anchors,
8067             //especially for anchored animations that change the el size.
8068
8069             var w, h, vp = false;
8070             if(!s){
8071                 var d = this.dom;
8072                 if(d == document.body || d == document){
8073                     vp = true;
8074                     w = D.getViewWidth(); h = D.getViewHeight();
8075                 }else{
8076                     w = this.getWidth(); h = this.getHeight();
8077                 }
8078             }else{
8079                 w = s.width;  h = s.height;
8080             }
8081             var x = 0, y = 0, r = Math.round;
8082             switch((anchor || "tl").toLowerCase()){
8083                 case "c":
8084                     x = r(w*.5);
8085                     y = r(h*.5);
8086                 break;
8087                 case "t":
8088                     x = r(w*.5);
8089                     y = 0;
8090                 break;
8091                 case "l":
8092                     x = 0;
8093                     y = r(h*.5);
8094                 break;
8095                 case "r":
8096                     x = w;
8097                     y = r(h*.5);
8098                 break;
8099                 case "b":
8100                     x = r(w*.5);
8101                     y = h;
8102                 break;
8103                 case "tl":
8104                     x = 0;
8105                     y = 0;
8106                 break;
8107                 case "bl":
8108                     x = 0;
8109                     y = h;
8110                 break;
8111                 case "br":
8112                     x = w;
8113                     y = h;
8114                 break;
8115                 case "tr":
8116                     x = w;
8117                     y = 0;
8118                 break;
8119             }
8120             if(local === true){
8121                 return [x, y];
8122             }
8123             if(vp){
8124                 var sc = this.getScroll();
8125                 return [x + sc.left, y + sc.top];
8126             }
8127             //Add the element's offset xy
8128             var o = this.getXY();
8129             return [x+o[0], y+o[1]];
8130         },
8131
8132         /**
8133          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8134          * supported position values.
8135          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8136          * @param {String} position The position to align to.
8137          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8138          * @return {Array} [x, y]
8139          */
8140         getAlignToXY : function(el, p, o){
8141             el = Roo.get(el);
8142             var d = this.dom;
8143             if(!el.dom){
8144                 throw "Element.alignTo with an element that doesn't exist";
8145             }
8146             var c = false; //constrain to viewport
8147             var p1 = "", p2 = "";
8148             o = o || [0,0];
8149
8150             if(!p){
8151                 p = "tl-bl";
8152             }else if(p == "?"){
8153                 p = "tl-bl?";
8154             }else if(p.indexOf("-") == -1){
8155                 p = "tl-" + p;
8156             }
8157             p = p.toLowerCase();
8158             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8159             if(!m){
8160                throw "Element.alignTo with an invalid alignment " + p;
8161             }
8162             p1 = m[1]; p2 = m[2]; c = !!m[3];
8163
8164             //Subtract the aligned el's internal xy from the target's offset xy
8165             //plus custom offset to get the aligned el's new offset xy
8166             var a1 = this.getAnchorXY(p1, true);
8167             var a2 = el.getAnchorXY(p2, false);
8168             var x = a2[0] - a1[0] + o[0];
8169             var y = a2[1] - a1[1] + o[1];
8170             if(c){
8171                 //constrain the aligned el to viewport if necessary
8172                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8173                 // 5px of margin for ie
8174                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8175
8176                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8177                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8178                 //otherwise swap the aligned el to the opposite border of the target.
8179                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8180                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8181                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8182                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8183
8184                var doc = document;
8185                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8186                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8187
8188                if((x+w) > dw + scrollX){
8189                     x = swapX ? r.left-w : dw+scrollX-w;
8190                 }
8191                if(x < scrollX){
8192                    x = swapX ? r.right : scrollX;
8193                }
8194                if((y+h) > dh + scrollY){
8195                     y = swapY ? r.top-h : dh+scrollY-h;
8196                 }
8197                if (y < scrollY){
8198                    y = swapY ? r.bottom : scrollY;
8199                }
8200             }
8201             return [x,y];
8202         },
8203
8204         // private
8205         getConstrainToXY : function(){
8206             var os = {top:0, left:0, bottom:0, right: 0};
8207
8208             return function(el, local, offsets, proposedXY){
8209                 el = Roo.get(el);
8210                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8211
8212                 var vw, vh, vx = 0, vy = 0;
8213                 if(el.dom == document.body || el.dom == document){
8214                     vw = Roo.lib.Dom.getViewWidth();
8215                     vh = Roo.lib.Dom.getViewHeight();
8216                 }else{
8217                     vw = el.dom.clientWidth;
8218                     vh = el.dom.clientHeight;
8219                     if(!local){
8220                         var vxy = el.getXY();
8221                         vx = vxy[0];
8222                         vy = vxy[1];
8223                     }
8224                 }
8225
8226                 var s = el.getScroll();
8227
8228                 vx += offsets.left + s.left;
8229                 vy += offsets.top + s.top;
8230
8231                 vw -= offsets.right;
8232                 vh -= offsets.bottom;
8233
8234                 var vr = vx+vw;
8235                 var vb = vy+vh;
8236
8237                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8238                 var x = xy[0], y = xy[1];
8239                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8240
8241                 // only move it if it needs it
8242                 var moved = false;
8243
8244                 // first validate right/bottom
8245                 if((x + w) > vr){
8246                     x = vr - w;
8247                     moved = true;
8248                 }
8249                 if((y + h) > vb){
8250                     y = vb - h;
8251                     moved = true;
8252                 }
8253                 // then make sure top/left isn't negative
8254                 if(x < vx){
8255                     x = vx;
8256                     moved = true;
8257                 }
8258                 if(y < vy){
8259                     y = vy;
8260                     moved = true;
8261                 }
8262                 return moved ? [x, y] : false;
8263             };
8264         }(),
8265
8266         // private
8267         adjustForConstraints : function(xy, parent, offsets){
8268             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8269         },
8270
8271         /**
8272          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8273          * document it aligns it to the viewport.
8274          * The position parameter is optional, and can be specified in any one of the following formats:
8275          * <ul>
8276          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8277          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8278          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8279          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8280          *   <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
8281          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8282          * </ul>
8283          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8284          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8285          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8286          * that specified in order to enforce the viewport constraints.
8287          * Following are all of the supported anchor positions:
8288     <pre>
8289     Value  Description
8290     -----  -----------------------------
8291     tl     The top left corner (default)
8292     t      The center of the top edge
8293     tr     The top right corner
8294     l      The center of the left edge
8295     c      In the center of the element
8296     r      The center of the right edge
8297     bl     The bottom left corner
8298     b      The center of the bottom edge
8299     br     The bottom right corner
8300     </pre>
8301     Example Usage:
8302     <pre><code>
8303     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8304     el.alignTo("other-el");
8305
8306     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8307     el.alignTo("other-el", "tr?");
8308
8309     // align the bottom right corner of el with the center left edge of other-el
8310     el.alignTo("other-el", "br-l?");
8311
8312     // align the center of el with the bottom left corner of other-el and
8313     // adjust the x position by -6 pixels (and the y position by 0)
8314     el.alignTo("other-el", "c-bl", [-6, 0]);
8315     </code></pre>
8316          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8317          * @param {String} position The position to align to.
8318          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8319          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8320          * @return {Roo.Element} this
8321          */
8322         alignTo : function(element, position, offsets, animate){
8323             var xy = this.getAlignToXY(element, position, offsets);
8324             this.setXY(xy, this.preanim(arguments, 3));
8325             return this;
8326         },
8327
8328         /**
8329          * Anchors an element to another element and realigns it when the window is resized.
8330          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8331          * @param {String} position The position to align to.
8332          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8333          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8334          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8335          * is a number, it is used as the buffer delay (defaults to 50ms).
8336          * @param {Function} callback The function to call after the animation finishes
8337          * @return {Roo.Element} this
8338          */
8339         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8340             var action = function(){
8341                 this.alignTo(el, alignment, offsets, animate);
8342                 Roo.callback(callback, this);
8343             };
8344             Roo.EventManager.onWindowResize(action, this);
8345             var tm = typeof monitorScroll;
8346             if(tm != 'undefined'){
8347                 Roo.EventManager.on(window, 'scroll', action, this,
8348                     {buffer: tm == 'number' ? monitorScroll : 50});
8349             }
8350             action.call(this); // align immediately
8351             return this;
8352         },
8353         /**
8354          * Clears any opacity settings from this element. Required in some cases for IE.
8355          * @return {Roo.Element} this
8356          */
8357         clearOpacity : function(){
8358             if (window.ActiveXObject) {
8359                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8360                     this.dom.style.filter = "";
8361                 }
8362             } else {
8363                 this.dom.style.opacity = "";
8364                 this.dom.style["-moz-opacity"] = "";
8365                 this.dom.style["-khtml-opacity"] = "";
8366             }
8367             return this;
8368         },
8369
8370         /**
8371          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8372          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8373          * @return {Roo.Element} this
8374          */
8375         hide : function(animate){
8376             this.setVisible(false, this.preanim(arguments, 0));
8377             return this;
8378         },
8379
8380         /**
8381         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8382         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8383          * @return {Roo.Element} this
8384          */
8385         show : function(animate){
8386             this.setVisible(true, this.preanim(arguments, 0));
8387             return this;
8388         },
8389
8390         /**
8391          * @private Test if size has a unit, otherwise appends the default
8392          */
8393         addUnits : function(size){
8394             return Roo.Element.addUnits(size, this.defaultUnit);
8395         },
8396
8397         /**
8398          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8399          * @return {Roo.Element} this
8400          */
8401         beginMeasure : function(){
8402             var el = this.dom;
8403             if(el.offsetWidth || el.offsetHeight){
8404                 return this; // offsets work already
8405             }
8406             var changed = [];
8407             var p = this.dom, b = document.body; // start with this element
8408             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8409                 var pe = Roo.get(p);
8410                 if(pe.getStyle('display') == 'none'){
8411                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8412                     p.style.visibility = "hidden";
8413                     p.style.display = "block";
8414                 }
8415                 p = p.parentNode;
8416             }
8417             this._measureChanged = changed;
8418             return this;
8419
8420         },
8421
8422         /**
8423          * Restores displays to before beginMeasure was called
8424          * @return {Roo.Element} this
8425          */
8426         endMeasure : function(){
8427             var changed = this._measureChanged;
8428             if(changed){
8429                 for(var i = 0, len = changed.length; i < len; i++) {
8430                     var r = changed[i];
8431                     r.el.style.visibility = r.visibility;
8432                     r.el.style.display = "none";
8433                 }
8434                 this._measureChanged = null;
8435             }
8436             return this;
8437         },
8438
8439         /**
8440         * Update the innerHTML of this element, optionally searching for and processing scripts
8441         * @param {String} html The new HTML
8442         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8443         * @param {Function} callback For async script loading you can be noticed when the update completes
8444         * @return {Roo.Element} this
8445          */
8446         update : function(html, loadScripts, callback){
8447             if(typeof html == "undefined"){
8448                 html = "";
8449             }
8450             if(loadScripts !== true){
8451                 this.dom.innerHTML = html;
8452                 if(typeof callback == "function"){
8453                     callback();
8454                 }
8455                 return this;
8456             }
8457             var id = Roo.id();
8458             var dom = this.dom;
8459
8460             html += '<span id="' + id + '"></span>';
8461
8462             E.onAvailable(id, function(){
8463                 var hd = document.getElementsByTagName("head")[0];
8464                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8465                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8466                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8467
8468                 var match;
8469                 while(match = re.exec(html)){
8470                     var attrs = match[1];
8471                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8472                     if(srcMatch && srcMatch[2]){
8473                        var s = document.createElement("script");
8474                        s.src = srcMatch[2];
8475                        var typeMatch = attrs.match(typeRe);
8476                        if(typeMatch && typeMatch[2]){
8477                            s.type = typeMatch[2];
8478                        }
8479                        hd.appendChild(s);
8480                     }else if(match[2] && match[2].length > 0){
8481                         if(window.execScript) {
8482                            window.execScript(match[2]);
8483                         } else {
8484                             /**
8485                              * eval:var:id
8486                              * eval:var:dom
8487                              * eval:var:html
8488                              * 
8489                              */
8490                            window.eval(match[2]);
8491                         }
8492                     }
8493                 }
8494                 var el = document.getElementById(id);
8495                 if(el){el.parentNode.removeChild(el);}
8496                 if(typeof callback == "function"){
8497                     callback();
8498                 }
8499             });
8500             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8501             return this;
8502         },
8503
8504         /**
8505          * Direct access to the UpdateManager update() method (takes the same parameters).
8506          * @param {String/Function} url The url for this request or a function to call to get the url
8507          * @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}
8508          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8509          * @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.
8510          * @return {Roo.Element} this
8511          */
8512         load : function(){
8513             var um = this.getUpdateManager();
8514             um.update.apply(um, arguments);
8515             return this;
8516         },
8517
8518         /**
8519         * Gets this element's UpdateManager
8520         * @return {Roo.UpdateManager} The UpdateManager
8521         */
8522         getUpdateManager : function(){
8523             if(!this.updateManager){
8524                 this.updateManager = new Roo.UpdateManager(this);
8525             }
8526             return this.updateManager;
8527         },
8528
8529         /**
8530          * Disables text selection for this element (normalized across browsers)
8531          * @return {Roo.Element} this
8532          */
8533         unselectable : function(){
8534             this.dom.unselectable = "on";
8535             this.swallowEvent("selectstart", true);
8536             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8537             this.addClass("x-unselectable");
8538             return this;
8539         },
8540
8541         /**
8542         * Calculates the x, y to center this element on the screen
8543         * @return {Array} The x, y values [x, y]
8544         */
8545         getCenterXY : function(){
8546             return this.getAlignToXY(document, 'c-c');
8547         },
8548
8549         /**
8550         * Centers the Element in either the viewport, or another Element.
8551         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8552         */
8553         center : function(centerIn){
8554             this.alignTo(centerIn || document, 'c-c');
8555             return this;
8556         },
8557
8558         /**
8559          * Tests various css rules/browsers to determine if this element uses a border box
8560          * @return {Boolean}
8561          */
8562         isBorderBox : function(){
8563             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8564         },
8565
8566         /**
8567          * Return a box {x, y, width, height} that can be used to set another elements
8568          * size/location to match this element.
8569          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8570          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8571          * @return {Object} box An object in the format {x, y, width, height}
8572          */
8573         getBox : function(contentBox, local){
8574             var xy;
8575             if(!local){
8576                 xy = this.getXY();
8577             }else{
8578                 var left = parseInt(this.getStyle("left"), 10) || 0;
8579                 var top = parseInt(this.getStyle("top"), 10) || 0;
8580                 xy = [left, top];
8581             }
8582             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8583             if(!contentBox){
8584                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8585             }else{
8586                 var l = this.getBorderWidth("l")+this.getPadding("l");
8587                 var r = this.getBorderWidth("r")+this.getPadding("r");
8588                 var t = this.getBorderWidth("t")+this.getPadding("t");
8589                 var b = this.getBorderWidth("b")+this.getPadding("b");
8590                 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)};
8591             }
8592             bx.right = bx.x + bx.width;
8593             bx.bottom = bx.y + bx.height;
8594             return bx;
8595         },
8596
8597         /**
8598          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8599          for more information about the sides.
8600          * @param {String} sides
8601          * @return {Number}
8602          */
8603         getFrameWidth : function(sides, onlyContentBox){
8604             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8605         },
8606
8607         /**
8608          * 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.
8609          * @param {Object} box The box to fill {x, y, width, height}
8610          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8611          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8612          * @return {Roo.Element} this
8613          */
8614         setBox : function(box, adjust, animate){
8615             var w = box.width, h = box.height;
8616             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8617                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8618                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8619             }
8620             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8621             return this;
8622         },
8623
8624         /**
8625          * Forces the browser to repaint this element
8626          * @return {Roo.Element} this
8627          */
8628          repaint : function(){
8629             var dom = this.dom;
8630             this.addClass("x-repaint");
8631             setTimeout(function(){
8632                 Roo.get(dom).removeClass("x-repaint");
8633             }, 1);
8634             return this;
8635         },
8636
8637         /**
8638          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8639          * then it returns the calculated width of the sides (see getPadding)
8640          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8641          * @return {Object/Number}
8642          */
8643         getMargins : function(side){
8644             if(!side){
8645                 return {
8646                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8647                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8648                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8649                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8650                 };
8651             }else{
8652                 return this.addStyles(side, El.margins);
8653              }
8654         },
8655
8656         // private
8657         addStyles : function(sides, styles){
8658             var val = 0, v, w;
8659             for(var i = 0, len = sides.length; i < len; i++){
8660                 v = this.getStyle(styles[sides.charAt(i)]);
8661                 if(v){
8662                      w = parseInt(v, 10);
8663                      if(w){ val += w; }
8664                 }
8665             }
8666             return val;
8667         },
8668
8669         /**
8670          * Creates a proxy element of this element
8671          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8672          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8673          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8674          * @return {Roo.Element} The new proxy element
8675          */
8676         createProxy : function(config, renderTo, matchBox){
8677             if(renderTo){
8678                 renderTo = Roo.getDom(renderTo);
8679             }else{
8680                 renderTo = document.body;
8681             }
8682             config = typeof config == "object" ?
8683                 config : {tag : "div", cls: config};
8684             var proxy = Roo.DomHelper.append(renderTo, config, true);
8685             if(matchBox){
8686                proxy.setBox(this.getBox());
8687             }
8688             return proxy;
8689         },
8690
8691         /**
8692          * Puts a mask over this element to disable user interaction. Requires core.css.
8693          * This method can only be applied to elements which accept child nodes.
8694          * @param {String} msg (optional) A message to display in the mask
8695          * @param {String} msgCls (optional) A css class to apply to the msg element
8696          * @return {Element} The mask  element
8697          */
8698         mask : function(msg, msgCls){
8699             if(this.getStyle("position") == "static"){
8700                 this.setStyle("position", "relative");
8701             }
8702             if(!this._mask){
8703                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8704             }
8705             this.addClass("x-masked");
8706             this._mask.setDisplayed(true);
8707             if(typeof msg == 'string'){
8708                 if(!this._maskMsg){
8709                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8710                 }
8711                 var mm = this._maskMsg;
8712                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8713                 mm.dom.firstChild.innerHTML = msg;
8714                 mm.setDisplayed(true);
8715                 mm.center(this);
8716             }
8717             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8718                 this._mask.setHeight(this.getHeight());
8719             }
8720             return this._mask;
8721         },
8722
8723         /**
8724          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8725          * it is cached for reuse.
8726          */
8727         unmask : function(removeEl){
8728             if(this._mask){
8729                 if(removeEl === true){
8730                     this._mask.remove();
8731                     delete this._mask;
8732                     if(this._maskMsg){
8733                         this._maskMsg.remove();
8734                         delete this._maskMsg;
8735                     }
8736                 }else{
8737                     this._mask.setDisplayed(false);
8738                     if(this._maskMsg){
8739                         this._maskMsg.setDisplayed(false);
8740                     }
8741                 }
8742             }
8743             this.removeClass("x-masked");
8744         },
8745
8746         /**
8747          * Returns true if this element is masked
8748          * @return {Boolean}
8749          */
8750         isMasked : function(){
8751             return this._mask && this._mask.isVisible();
8752         },
8753
8754         /**
8755          * Creates an iframe shim for this element to keep selects and other windowed objects from
8756          * showing through.
8757          * @return {Roo.Element} The new shim element
8758          */
8759         createShim : function(){
8760             var el = document.createElement('iframe');
8761             el.frameBorder = 'no';
8762             el.className = 'roo-shim';
8763             if(Roo.isIE && Roo.isSecure){
8764                 el.src = Roo.SSL_SECURE_URL;
8765             }
8766             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8767             shim.autoBoxAdjust = false;
8768             return shim;
8769         },
8770
8771         /**
8772          * Removes this element from the DOM and deletes it from the cache
8773          */
8774         remove : function(){
8775             if(this.dom.parentNode){
8776                 this.dom.parentNode.removeChild(this.dom);
8777             }
8778             delete El.cache[this.dom.id];
8779         },
8780
8781         /**
8782          * Sets up event handlers to add and remove a css class when the mouse is over this element
8783          * @param {String} className
8784          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8785          * mouseout events for children elements
8786          * @return {Roo.Element} this
8787          */
8788         addClassOnOver : function(className, preventFlicker){
8789             this.on("mouseover", function(){
8790                 Roo.fly(this, '_internal').addClass(className);
8791             }, this.dom);
8792             var removeFn = function(e){
8793                 if(preventFlicker !== true || !e.within(this, true)){
8794                     Roo.fly(this, '_internal').removeClass(className);
8795                 }
8796             };
8797             this.on("mouseout", removeFn, this.dom);
8798             return this;
8799         },
8800
8801         /**
8802          * Sets up event handlers to add and remove a css class when this element has the focus
8803          * @param {String} className
8804          * @return {Roo.Element} this
8805          */
8806         addClassOnFocus : function(className){
8807             this.on("focus", function(){
8808                 Roo.fly(this, '_internal').addClass(className);
8809             }, this.dom);
8810             this.on("blur", function(){
8811                 Roo.fly(this, '_internal').removeClass(className);
8812             }, this.dom);
8813             return this;
8814         },
8815         /**
8816          * 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)
8817          * @param {String} className
8818          * @return {Roo.Element} this
8819          */
8820         addClassOnClick : function(className){
8821             var dom = this.dom;
8822             this.on("mousedown", function(){
8823                 Roo.fly(dom, '_internal').addClass(className);
8824                 var d = Roo.get(document);
8825                 var fn = function(){
8826                     Roo.fly(dom, '_internal').removeClass(className);
8827                     d.removeListener("mouseup", fn);
8828                 };
8829                 d.on("mouseup", fn);
8830             });
8831             return this;
8832         },
8833
8834         /**
8835          * Stops the specified event from bubbling and optionally prevents the default action
8836          * @param {String} eventName
8837          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8838          * @return {Roo.Element} this
8839          */
8840         swallowEvent : function(eventName, preventDefault){
8841             var fn = function(e){
8842                 e.stopPropagation();
8843                 if(preventDefault){
8844                     e.preventDefault();
8845                 }
8846             };
8847             if(eventName instanceof Array){
8848                 for(var i = 0, len = eventName.length; i < len; i++){
8849                      this.on(eventName[i], fn);
8850                 }
8851                 return this;
8852             }
8853             this.on(eventName, fn);
8854             return this;
8855         },
8856
8857         /**
8858          * @private
8859          */
8860       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8861
8862         /**
8863          * Sizes this element to its parent element's dimensions performing
8864          * neccessary box adjustments.
8865          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8866          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8867          * @return {Roo.Element} this
8868          */
8869         fitToParent : function(monitorResize, targetParent) {
8870           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8871           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8872           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8873             return;
8874           }
8875           var p = Roo.get(targetParent || this.dom.parentNode);
8876           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8877           if (monitorResize === true) {
8878             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8879             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8880           }
8881           return this;
8882         },
8883
8884         /**
8885          * Gets the next sibling, skipping text nodes
8886          * @return {HTMLElement} The next sibling or null
8887          */
8888         getNextSibling : function(){
8889             var n = this.dom.nextSibling;
8890             while(n && n.nodeType != 1){
8891                 n = n.nextSibling;
8892             }
8893             return n;
8894         },
8895
8896         /**
8897          * Gets the previous sibling, skipping text nodes
8898          * @return {HTMLElement} The previous sibling or null
8899          */
8900         getPrevSibling : function(){
8901             var n = this.dom.previousSibling;
8902             while(n && n.nodeType != 1){
8903                 n = n.previousSibling;
8904             }
8905             return n;
8906         },
8907
8908
8909         /**
8910          * Appends the passed element(s) to this element
8911          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8912          * @return {Roo.Element} this
8913          */
8914         appendChild: function(el){
8915             el = Roo.get(el);
8916             el.appendTo(this);
8917             return this;
8918         },
8919
8920         /**
8921          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8922          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8923          * automatically generated with the specified attributes.
8924          * @param {HTMLElement} insertBefore (optional) a child element of this element
8925          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8926          * @return {Roo.Element} The new child element
8927          */
8928         createChild: function(config, insertBefore, returnDom){
8929             config = config || {tag:'div'};
8930             if(insertBefore){
8931                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8932             }
8933             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8934         },
8935
8936         /**
8937          * Appends this element to the passed element
8938          * @param {String/HTMLElement/Element} el The new parent element
8939          * @return {Roo.Element} this
8940          */
8941         appendTo: function(el){
8942             el = Roo.getDom(el);
8943             el.appendChild(this.dom);
8944             return this;
8945         },
8946
8947         /**
8948          * Inserts this element before the passed element in the DOM
8949          * @param {String/HTMLElement/Element} el The element to insert before
8950          * @return {Roo.Element} this
8951          */
8952         insertBefore: function(el){
8953             el = Roo.getDom(el);
8954             el.parentNode.insertBefore(this.dom, el);
8955             return this;
8956         },
8957
8958         /**
8959          * Inserts this element after the passed element in the DOM
8960          * @param {String/HTMLElement/Element} el The element to insert after
8961          * @return {Roo.Element} this
8962          */
8963         insertAfter: function(el){
8964             el = Roo.getDom(el);
8965             el.parentNode.insertBefore(this.dom, el.nextSibling);
8966             return this;
8967         },
8968
8969         /**
8970          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8971          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8972          * @return {Roo.Element} The new child
8973          */
8974         insertFirst: function(el, returnDom){
8975             el = el || {};
8976             if(typeof el == 'object' && !el.nodeType){ // dh config
8977                 return this.createChild(el, this.dom.firstChild, returnDom);
8978             }else{
8979                 el = Roo.getDom(el);
8980                 this.dom.insertBefore(el, this.dom.firstChild);
8981                 return !returnDom ? Roo.get(el) : el;
8982             }
8983         },
8984
8985         /**
8986          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8987          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8988          * @param {String} where (optional) 'before' or 'after' defaults to before
8989          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8990          * @return {Roo.Element} the inserted Element
8991          */
8992         insertSibling: function(el, where, returnDom){
8993             where = where ? where.toLowerCase() : 'before';
8994             el = el || {};
8995             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8996
8997             if(typeof el == 'object' && !el.nodeType){ // dh config
8998                 if(where == 'after' && !this.dom.nextSibling){
8999                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9000                 }else{
9001                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9002                 }
9003
9004             }else{
9005                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9006                             where == 'before' ? this.dom : this.dom.nextSibling);
9007                 if(!returnDom){
9008                     rt = Roo.get(rt);
9009                 }
9010             }
9011             return rt;
9012         },
9013
9014         /**
9015          * Creates and wraps this element with another element
9016          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9017          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9018          * @return {HTMLElement/Element} The newly created wrapper element
9019          */
9020         wrap: function(config, returnDom){
9021             if(!config){
9022                 config = {tag: "div"};
9023             }
9024             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9025             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9026             return newEl;
9027         },
9028
9029         /**
9030          * Replaces the passed element with this element
9031          * @param {String/HTMLElement/Element} el The element to replace
9032          * @return {Roo.Element} this
9033          */
9034         replace: function(el){
9035             el = Roo.get(el);
9036             this.insertBefore(el);
9037             el.remove();
9038             return this;
9039         },
9040
9041         /**
9042          * Inserts an html fragment into this element
9043          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9044          * @param {String} html The HTML fragment
9045          * @param {Boolean} returnEl True to return an Roo.Element
9046          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9047          */
9048         insertHtml : function(where, html, returnEl){
9049             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9050             return returnEl ? Roo.get(el) : el;
9051         },
9052
9053         /**
9054          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9055          * @param {Object} o The object with the attributes
9056          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9057          * @return {Roo.Element} this
9058          */
9059         set : function(o, useSet){
9060             var el = this.dom;
9061             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9062             for(var attr in o){
9063                 if(attr == "style" || typeof o[attr] == "function") continue;
9064                 if(attr=="cls"){
9065                     el.className = o["cls"];
9066                 }else{
9067                     if(useSet) el.setAttribute(attr, o[attr]);
9068                     else el[attr] = o[attr];
9069                 }
9070             }
9071             if(o.style){
9072                 Roo.DomHelper.applyStyles(el, o.style);
9073             }
9074             return this;
9075         },
9076
9077         /**
9078          * Convenience method for constructing a KeyMap
9079          * @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:
9080          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9081          * @param {Function} fn The function to call
9082          * @param {Object} scope (optional) The scope of the function
9083          * @return {Roo.KeyMap} The KeyMap created
9084          */
9085         addKeyListener : function(key, fn, scope){
9086             var config;
9087             if(typeof key != "object" || key instanceof Array){
9088                 config = {
9089                     key: key,
9090                     fn: fn,
9091                     scope: scope
9092                 };
9093             }else{
9094                 config = {
9095                     key : key.key,
9096                     shift : key.shift,
9097                     ctrl : key.ctrl,
9098                     alt : key.alt,
9099                     fn: fn,
9100                     scope: scope
9101                 };
9102             }
9103             return new Roo.KeyMap(this, config);
9104         },
9105
9106         /**
9107          * Creates a KeyMap for this element
9108          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9109          * @return {Roo.KeyMap} The KeyMap created
9110          */
9111         addKeyMap : function(config){
9112             return new Roo.KeyMap(this, config);
9113         },
9114
9115         /**
9116          * Returns true if this element is scrollable.
9117          * @return {Boolean}
9118          */
9119          isScrollable : function(){
9120             var dom = this.dom;
9121             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9122         },
9123
9124         /**
9125          * 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().
9126          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9127          * @param {Number} value The new scroll value
9128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9129          * @return {Element} this
9130          */
9131
9132         scrollTo : function(side, value, animate){
9133             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9134             if(!animate || !A){
9135                 this.dom[prop] = value;
9136             }else{
9137                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9138                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9139             }
9140             return this;
9141         },
9142
9143         /**
9144          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9145          * within this element's scrollable range.
9146          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9147          * @param {Number} distance How far to scroll the element in pixels
9148          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9149          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9150          * was scrolled as far as it could go.
9151          */
9152          scroll : function(direction, distance, animate){
9153              if(!this.isScrollable()){
9154                  return;
9155              }
9156              var el = this.dom;
9157              var l = el.scrollLeft, t = el.scrollTop;
9158              var w = el.scrollWidth, h = el.scrollHeight;
9159              var cw = el.clientWidth, ch = el.clientHeight;
9160              direction = direction.toLowerCase();
9161              var scrolled = false;
9162              var a = this.preanim(arguments, 2);
9163              switch(direction){
9164                  case "l":
9165                  case "left":
9166                      if(w - l > cw){
9167                          var v = Math.min(l + distance, w-cw);
9168                          this.scrollTo("left", v, a);
9169                          scrolled = true;
9170                      }
9171                      break;
9172                 case "r":
9173                 case "right":
9174                      if(l > 0){
9175                          var v = Math.max(l - distance, 0);
9176                          this.scrollTo("left", v, a);
9177                          scrolled = true;
9178                      }
9179                      break;
9180                 case "t":
9181                 case "top":
9182                 case "up":
9183                      if(t > 0){
9184                          var v = Math.max(t - distance, 0);
9185                          this.scrollTo("top", v, a);
9186                          scrolled = true;
9187                      }
9188                      break;
9189                 case "b":
9190                 case "bottom":
9191                 case "down":
9192                      if(h - t > ch){
9193                          var v = Math.min(t + distance, h-ch);
9194                          this.scrollTo("top", v, a);
9195                          scrolled = true;
9196                      }
9197                      break;
9198              }
9199              return scrolled;
9200         },
9201
9202         /**
9203          * Translates the passed page coordinates into left/top css values for this element
9204          * @param {Number/Array} x The page x or an array containing [x, y]
9205          * @param {Number} y The page y
9206          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9207          */
9208         translatePoints : function(x, y){
9209             if(typeof x == 'object' || x instanceof Array){
9210                 y = x[1]; x = x[0];
9211             }
9212             var p = this.getStyle('position');
9213             var o = this.getXY();
9214
9215             var l = parseInt(this.getStyle('left'), 10);
9216             var t = parseInt(this.getStyle('top'), 10);
9217
9218             if(isNaN(l)){
9219                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9220             }
9221             if(isNaN(t)){
9222                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9223             }
9224
9225             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9226         },
9227
9228         /**
9229          * Returns the current scroll position of the element.
9230          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9231          */
9232         getScroll : function(){
9233             var d = this.dom, doc = document;
9234             if(d == doc || d == doc.body){
9235                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9236                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9237                 return {left: l, top: t};
9238             }else{
9239                 return {left: d.scrollLeft, top: d.scrollTop};
9240             }
9241         },
9242
9243         /**
9244          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9245          * are convert to standard 6 digit hex color.
9246          * @param {String} attr The css attribute
9247          * @param {String} defaultValue The default value to use when a valid color isn't found
9248          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9249          * YUI color anims.
9250          */
9251         getColor : function(attr, defaultValue, prefix){
9252             var v = this.getStyle(attr);
9253             if(!v || v == "transparent" || v == "inherit") {
9254                 return defaultValue;
9255             }
9256             var color = typeof prefix == "undefined" ? "#" : prefix;
9257             if(v.substr(0, 4) == "rgb("){
9258                 var rvs = v.slice(4, v.length -1).split(",");
9259                 for(var i = 0; i < 3; i++){
9260                     var h = parseInt(rvs[i]).toString(16);
9261                     if(h < 16){
9262                         h = "0" + h;
9263                     }
9264                     color += h;
9265                 }
9266             } else {
9267                 if(v.substr(0, 1) == "#"){
9268                     if(v.length == 4) {
9269                         for(var i = 1; i < 4; i++){
9270                             var c = v.charAt(i);
9271                             color +=  c + c;
9272                         }
9273                     }else if(v.length == 7){
9274                         color += v.substr(1);
9275                     }
9276                 }
9277             }
9278             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9279         },
9280
9281         /**
9282          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9283          * gradient background, rounded corners and a 4-way shadow.
9284          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9285          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9286          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9287          * @return {Roo.Element} this
9288          */
9289         boxWrap : function(cls){
9290             cls = cls || 'x-box';
9291             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9292             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9293             return el;
9294         },
9295
9296         /**
9297          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9298          * @param {String} namespace The namespace in which to look for the attribute
9299          * @param {String} name The attribute name
9300          * @return {String} The attribute value
9301          */
9302         getAttributeNS : Roo.isIE ? function(ns, name){
9303             var d = this.dom;
9304             var type = typeof d[ns+":"+name];
9305             if(type != 'undefined' && type != 'unknown'){
9306                 return d[ns+":"+name];
9307             }
9308             return d[name];
9309         } : function(ns, name){
9310             var d = this.dom;
9311             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9312         }
9313     };
9314
9315     var ep = El.prototype;
9316
9317     /**
9318      * Appends an event handler (Shorthand for addListener)
9319      * @param {String}   eventName     The type of event to append
9320      * @param {Function} fn        The method the event invokes
9321      * @param {Object} scope       (optional) The scope (this object) of the fn
9322      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9323      * @method
9324      */
9325     ep.on = ep.addListener;
9326         // backwards compat
9327     ep.mon = ep.addListener;
9328
9329     /**
9330      * Removes an event handler from this element (shorthand for removeListener)
9331      * @param {String} eventName the type of event to remove
9332      * @param {Function} fn the method the event invokes
9333      * @return {Roo.Element} this
9334      * @method
9335      */
9336     ep.un = ep.removeListener;
9337
9338     /**
9339      * true to automatically adjust width and height settings for box-model issues (default to true)
9340      */
9341     ep.autoBoxAdjust = true;
9342
9343     // private
9344     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9345
9346     // private
9347     El.addUnits = function(v, defaultUnit){
9348         if(v === "" || v == "auto"){
9349             return v;
9350         }
9351         if(v === undefined){
9352             return '';
9353         }
9354         if(typeof v == "number" || !El.unitPattern.test(v)){
9355             return v + (defaultUnit || 'px');
9356         }
9357         return v;
9358     };
9359
9360     // special markup used throughout Roo when box wrapping elements
9361     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>';
9362     /**
9363      * Visibility mode constant - Use visibility to hide element
9364      * @static
9365      * @type Number
9366      */
9367     El.VISIBILITY = 1;
9368     /**
9369      * Visibility mode constant - Use display to hide element
9370      * @static
9371      * @type Number
9372      */
9373     El.DISPLAY = 2;
9374
9375     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9376     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9377     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9378
9379
9380
9381     /**
9382      * @private
9383      */
9384     El.cache = {};
9385
9386     var docEl;
9387
9388     /**
9389      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9390      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9391      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9392      * @return {Element} The Element object
9393      * @static
9394      */
9395     El.get = function(el){
9396         var ex, elm, id;
9397         if(!el){ return null; }
9398         if(typeof el == "string"){ // element id
9399             if(!(elm = document.getElementById(el))){
9400                 return null;
9401             }
9402             if(ex = El.cache[el]){
9403                 ex.dom = elm;
9404             }else{
9405                 ex = El.cache[el] = new El(elm);
9406             }
9407             return ex;
9408         }else if(el.tagName){ // dom element
9409             if(!(id = el.id)){
9410                 id = Roo.id(el);
9411             }
9412             if(ex = El.cache[id]){
9413                 ex.dom = el;
9414             }else{
9415                 ex = El.cache[id] = new El(el);
9416             }
9417             return ex;
9418         }else if(el instanceof El){
9419             if(el != docEl){
9420                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9421                                                               // catch case where it hasn't been appended
9422                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9423             }
9424             return el;
9425         }else if(el.isComposite){
9426             return el;
9427         }else if(el instanceof Array){
9428             return El.select(el);
9429         }else if(el == document){
9430             // create a bogus element object representing the document object
9431             if(!docEl){
9432                 var f = function(){};
9433                 f.prototype = El.prototype;
9434                 docEl = new f();
9435                 docEl.dom = document;
9436             }
9437             return docEl;
9438         }
9439         return null;
9440     };
9441
9442     // private
9443     El.uncache = function(el){
9444         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9445             if(a[i]){
9446                 delete El.cache[a[i].id || a[i]];
9447             }
9448         }
9449     };
9450
9451     // private
9452     // Garbage collection - uncache elements/purge listeners on orphaned elements
9453     // so we don't hold a reference and cause the browser to retain them
9454     El.garbageCollect = function(){
9455         if(!Roo.enableGarbageCollector){
9456             clearInterval(El.collectorThread);
9457             return;
9458         }
9459         for(var eid in El.cache){
9460             var el = El.cache[eid], d = el.dom;
9461             // -------------------------------------------------------
9462             // Determining what is garbage:
9463             // -------------------------------------------------------
9464             // !d
9465             // dom node is null, definitely garbage
9466             // -------------------------------------------------------
9467             // !d.parentNode
9468             // no parentNode == direct orphan, definitely garbage
9469             // -------------------------------------------------------
9470             // !d.offsetParent && !document.getElementById(eid)
9471             // display none elements have no offsetParent so we will
9472             // also try to look it up by it's id. However, check
9473             // offsetParent first so we don't do unneeded lookups.
9474             // This enables collection of elements that are not orphans
9475             // directly, but somewhere up the line they have an orphan
9476             // parent.
9477             // -------------------------------------------------------
9478             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9479                 delete El.cache[eid];
9480                 if(d && Roo.enableListenerCollection){
9481                     E.purgeElement(d);
9482                 }
9483             }
9484         }
9485     }
9486     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9487
9488
9489     // dom is optional
9490     El.Flyweight = function(dom){
9491         this.dom = dom;
9492     };
9493     El.Flyweight.prototype = El.prototype;
9494
9495     El._flyweights = {};
9496     /**
9497      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9498      * the dom node can be overwritten by other code.
9499      * @param {String/HTMLElement} el The dom node or id
9500      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9501      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9502      * @static
9503      * @return {Element} The shared Element object
9504      */
9505     El.fly = function(el, named){
9506         named = named || '_global';
9507         el = Roo.getDom(el);
9508         if(!el){
9509             return null;
9510         }
9511         if(!El._flyweights[named]){
9512             El._flyweights[named] = new El.Flyweight();
9513         }
9514         El._flyweights[named].dom = el;
9515         return El._flyweights[named];
9516     };
9517
9518     /**
9519      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9520      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9521      * Shorthand of {@link Roo.Element#get}
9522      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9523      * @return {Element} The Element object
9524      * @member Roo
9525      * @method get
9526      */
9527     Roo.get = El.get;
9528     /**
9529      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9530      * the dom node can be overwritten by other code.
9531      * Shorthand of {@link Roo.Element#fly}
9532      * @param {String/HTMLElement} el The dom node or id
9533      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9534      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9535      * @static
9536      * @return {Element} The shared Element object
9537      * @member Roo
9538      * @method fly
9539      */
9540     Roo.fly = El.fly;
9541
9542     // speedy lookup for elements never to box adjust
9543     var noBoxAdjust = Roo.isStrict ? {
9544         select:1
9545     } : {
9546         input:1, select:1, textarea:1
9547     };
9548     if(Roo.isIE || Roo.isGecko){
9549         noBoxAdjust['button'] = 1;
9550     }
9551
9552
9553     Roo.EventManager.on(window, 'unload', function(){
9554         delete El.cache;
9555         delete El._flyweights;
9556     });
9557 })();
9558
9559
9560
9561
9562 if(Roo.DomQuery){
9563     Roo.Element.selectorFunction = Roo.DomQuery.select;
9564 }
9565
9566 Roo.Element.select = function(selector, unique, root){
9567     var els;
9568     if(typeof selector == "string"){
9569         els = Roo.Element.selectorFunction(selector, root);
9570     }else if(selector.length !== undefined){
9571         els = selector;
9572     }else{
9573         throw "Invalid selector";
9574     }
9575     if(unique === true){
9576         return new Roo.CompositeElement(els);
9577     }else{
9578         return new Roo.CompositeElementLite(els);
9579     }
9580 };
9581 /**
9582  * Selects elements based on the passed CSS selector to enable working on them as 1.
9583  * @param {String/Array} selector The CSS selector or an array of elements
9584  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9585  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9586  * @return {CompositeElementLite/CompositeElement}
9587  * @member Roo
9588  * @method select
9589  */
9590 Roo.select = Roo.Element.select;
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605 /*
9606  * Based on:
9607  * Ext JS Library 1.1.1
9608  * Copyright(c) 2006-2007, Ext JS, LLC.
9609  *
9610  * Originally Released Under LGPL - original licence link has changed is not relivant.
9611  *
9612  * Fork - LGPL
9613  * <script type="text/javascript">
9614  */
9615
9616
9617
9618 //Notifies Element that fx methods are available
9619 Roo.enableFx = true;
9620
9621 /**
9622  * @class Roo.Fx
9623  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9624  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9625  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9626  * Element effects to work.</p><br/>
9627  *
9628  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9629  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9630  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9631  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9632  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9633  * expected results and should be done with care.</p><br/>
9634  *
9635  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9636  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9637 <pre>
9638 Value  Description
9639 -----  -----------------------------
9640 tl     The top left corner
9641 t      The center of the top edge
9642 tr     The top right corner
9643 l      The center of the left edge
9644 r      The center of the right edge
9645 bl     The bottom left corner
9646 b      The center of the bottom edge
9647 br     The bottom right corner
9648 </pre>
9649  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9650  * below are common options that can be passed to any Fx method.</b>
9651  * @cfg {Function} callback A function called when the effect is finished
9652  * @cfg {Object} scope The scope of the effect function
9653  * @cfg {String} easing A valid Easing value for the effect
9654  * @cfg {String} afterCls A css class to apply after the effect
9655  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9656  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9657  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9658  * effects that end with the element being visually hidden, ignored otherwise)
9659  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9660  * a function which returns such a specification that will be applied to the Element after the effect finishes
9661  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9662  * @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
9663  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9664  */
9665 Roo.Fx = {
9666         /**
9667          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9668          * origin for the slide effect.  This function automatically handles wrapping the element with
9669          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9670          * Usage:
9671          *<pre><code>
9672 // default: slide the element in from the top
9673 el.slideIn();
9674
9675 // custom: slide the element in from the right with a 2-second duration
9676 el.slideIn('r', { duration: 2 });
9677
9678 // common config options shown with default values
9679 el.slideIn('t', {
9680     easing: 'easeOut',
9681     duration: .5
9682 });
9683 </code></pre>
9684          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9685          * @param {Object} options (optional) Object literal with any of the Fx config options
9686          * @return {Roo.Element} The Element
9687          */
9688     slideIn : function(anchor, o){
9689         var el = this.getFxEl();
9690         o = o || {};
9691
9692         el.queueFx(o, function(){
9693
9694             anchor = anchor || "t";
9695
9696             // fix display to visibility
9697             this.fixDisplay();
9698
9699             // restore values after effect
9700             var r = this.getFxRestore();
9701             var b = this.getBox();
9702             // fixed size for slide
9703             this.setSize(b);
9704
9705             // wrap if needed
9706             var wrap = this.fxWrap(r.pos, o, "hidden");
9707
9708             var st = this.dom.style;
9709             st.visibility = "visible";
9710             st.position = "absolute";
9711
9712             // clear out temp styles after slide and unwrap
9713             var after = function(){
9714                 el.fxUnwrap(wrap, r.pos, o);
9715                 st.width = r.width;
9716                 st.height = r.height;
9717                 el.afterFx(o);
9718             };
9719             // time to calc the positions
9720             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9721
9722             switch(anchor.toLowerCase()){
9723                 case "t":
9724                     wrap.setSize(b.width, 0);
9725                     st.left = st.bottom = "0";
9726                     a = {height: bh};
9727                 break;
9728                 case "l":
9729                     wrap.setSize(0, b.height);
9730                     st.right = st.top = "0";
9731                     a = {width: bw};
9732                 break;
9733                 case "r":
9734                     wrap.setSize(0, b.height);
9735                     wrap.setX(b.right);
9736                     st.left = st.top = "0";
9737                     a = {width: bw, points: pt};
9738                 break;
9739                 case "b":
9740                     wrap.setSize(b.width, 0);
9741                     wrap.setY(b.bottom);
9742                     st.left = st.top = "0";
9743                     a = {height: bh, points: pt};
9744                 break;
9745                 case "tl":
9746                     wrap.setSize(0, 0);
9747                     st.right = st.bottom = "0";
9748                     a = {width: bw, height: bh};
9749                 break;
9750                 case "bl":
9751                     wrap.setSize(0, 0);
9752                     wrap.setY(b.y+b.height);
9753                     st.right = st.top = "0";
9754                     a = {width: bw, height: bh, points: pt};
9755                 break;
9756                 case "br":
9757                     wrap.setSize(0, 0);
9758                     wrap.setXY([b.right, b.bottom]);
9759                     st.left = st.top = "0";
9760                     a = {width: bw, height: bh, points: pt};
9761                 break;
9762                 case "tr":
9763                     wrap.setSize(0, 0);
9764                     wrap.setX(b.x+b.width);
9765                     st.left = st.bottom = "0";
9766                     a = {width: bw, height: bh, points: pt};
9767                 break;
9768             }
9769             this.dom.style.visibility = "visible";
9770             wrap.show();
9771
9772             arguments.callee.anim = wrap.fxanim(a,
9773                 o,
9774                 'motion',
9775                 .5,
9776                 'easeOut', after);
9777         });
9778         return this;
9779     },
9780     
9781         /**
9782          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9783          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9784          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9785          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9786          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9787          * Usage:
9788          *<pre><code>
9789 // default: slide the element out to the top
9790 el.slideOut();
9791
9792 // custom: slide the element out to the right with a 2-second duration
9793 el.slideOut('r', { duration: 2 });
9794
9795 // common config options shown with default values
9796 el.slideOut('t', {
9797     easing: 'easeOut',
9798     duration: .5,
9799     remove: false,
9800     useDisplay: false
9801 });
9802 </code></pre>
9803          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9804          * @param {Object} options (optional) Object literal with any of the Fx config options
9805          * @return {Roo.Element} The Element
9806          */
9807     slideOut : function(anchor, o){
9808         var el = this.getFxEl();
9809         o = o || {};
9810
9811         el.queueFx(o, function(){
9812
9813             anchor = anchor || "t";
9814
9815             // restore values after effect
9816             var r = this.getFxRestore();
9817             
9818             var b = this.getBox();
9819             // fixed size for slide
9820             this.setSize(b);
9821
9822             // wrap if needed
9823             var wrap = this.fxWrap(r.pos, o, "visible");
9824
9825             var st = this.dom.style;
9826             st.visibility = "visible";
9827             st.position = "absolute";
9828
9829             wrap.setSize(b);
9830
9831             var after = function(){
9832                 if(o.useDisplay){
9833                     el.setDisplayed(false);
9834                 }else{
9835                     el.hide();
9836                 }
9837
9838                 el.fxUnwrap(wrap, r.pos, o);
9839
9840                 st.width = r.width;
9841                 st.height = r.height;
9842
9843                 el.afterFx(o);
9844             };
9845
9846             var a, zero = {to: 0};
9847             switch(anchor.toLowerCase()){
9848                 case "t":
9849                     st.left = st.bottom = "0";
9850                     a = {height: zero};
9851                 break;
9852                 case "l":
9853                     st.right = st.top = "0";
9854                     a = {width: zero};
9855                 break;
9856                 case "r":
9857                     st.left = st.top = "0";
9858                     a = {width: zero, points: {to:[b.right, b.y]}};
9859                 break;
9860                 case "b":
9861                     st.left = st.top = "0";
9862                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9863                 break;
9864                 case "tl":
9865                     st.right = st.bottom = "0";
9866                     a = {width: zero, height: zero};
9867                 break;
9868                 case "bl":
9869                     st.right = st.top = "0";
9870                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9871                 break;
9872                 case "br":
9873                     st.left = st.top = "0";
9874                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9875                 break;
9876                 case "tr":
9877                     st.left = st.bottom = "0";
9878                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9879                 break;
9880             }
9881
9882             arguments.callee.anim = wrap.fxanim(a,
9883                 o,
9884                 'motion',
9885                 .5,
9886                 "easeOut", after);
9887         });
9888         return this;
9889     },
9890
9891         /**
9892          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9893          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9894          * The element must be removed from the DOM using the 'remove' config option if desired.
9895          * Usage:
9896          *<pre><code>
9897 // default
9898 el.puff();
9899
9900 // common config options shown with default values
9901 el.puff({
9902     easing: 'easeOut',
9903     duration: .5,
9904     remove: false,
9905     useDisplay: false
9906 });
9907 </code></pre>
9908          * @param {Object} options (optional) Object literal with any of the Fx config options
9909          * @return {Roo.Element} The Element
9910          */
9911     puff : function(o){
9912         var el = this.getFxEl();
9913         o = o || {};
9914
9915         el.queueFx(o, function(){
9916             this.clearOpacity();
9917             this.show();
9918
9919             // restore values after effect
9920             var r = this.getFxRestore();
9921             var st = this.dom.style;
9922
9923             var after = function(){
9924                 if(o.useDisplay){
9925                     el.setDisplayed(false);
9926                 }else{
9927                     el.hide();
9928                 }
9929
9930                 el.clearOpacity();
9931
9932                 el.setPositioning(r.pos);
9933                 st.width = r.width;
9934                 st.height = r.height;
9935                 st.fontSize = '';
9936                 el.afterFx(o);
9937             };
9938
9939             var width = this.getWidth();
9940             var height = this.getHeight();
9941
9942             arguments.callee.anim = this.fxanim({
9943                     width : {to: this.adjustWidth(width * 2)},
9944                     height : {to: this.adjustHeight(height * 2)},
9945                     points : {by: [-(width * .5), -(height * .5)]},
9946                     opacity : {to: 0},
9947                     fontSize: {to:200, unit: "%"}
9948                 },
9949                 o,
9950                 'motion',
9951                 .5,
9952                 "easeOut", after);
9953         });
9954         return this;
9955     },
9956
9957         /**
9958          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9959          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9960          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9961          * Usage:
9962          *<pre><code>
9963 // default
9964 el.switchOff();
9965
9966 // all config options shown with default values
9967 el.switchOff({
9968     easing: 'easeIn',
9969     duration: .3,
9970     remove: false,
9971     useDisplay: false
9972 });
9973 </code></pre>
9974          * @param {Object} options (optional) Object literal with any of the Fx config options
9975          * @return {Roo.Element} The Element
9976          */
9977     switchOff : function(o){
9978         var el = this.getFxEl();
9979         o = o || {};
9980
9981         el.queueFx(o, function(){
9982             this.clearOpacity();
9983             this.clip();
9984
9985             // restore values after effect
9986             var r = this.getFxRestore();
9987             var st = this.dom.style;
9988
9989             var after = function(){
9990                 if(o.useDisplay){
9991                     el.setDisplayed(false);
9992                 }else{
9993                     el.hide();
9994                 }
9995
9996                 el.clearOpacity();
9997                 el.setPositioning(r.pos);
9998                 st.width = r.width;
9999                 st.height = r.height;
10000
10001                 el.afterFx(o);
10002             };
10003
10004             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10005                 this.clearOpacity();
10006                 (function(){
10007                     this.fxanim({
10008                         height:{to:1},
10009                         points:{by:[0, this.getHeight() * .5]}
10010                     }, o, 'motion', 0.3, 'easeIn', after);
10011                 }).defer(100, this);
10012             });
10013         });
10014         return this;
10015     },
10016
10017     /**
10018      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10019      * changed using the "attr" config option) and then fading back to the original color. If no original
10020      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10021      * Usage:
10022 <pre><code>
10023 // default: highlight background to yellow
10024 el.highlight();
10025
10026 // custom: highlight foreground text to blue for 2 seconds
10027 el.highlight("0000ff", { attr: 'color', duration: 2 });
10028
10029 // common config options shown with default values
10030 el.highlight("ffff9c", {
10031     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10032     endColor: (current color) or "ffffff",
10033     easing: 'easeIn',
10034     duration: 1
10035 });
10036 </code></pre>
10037      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10038      * @param {Object} options (optional) Object literal with any of the Fx config options
10039      * @return {Roo.Element} The Element
10040      */ 
10041     highlight : function(color, o){
10042         var el = this.getFxEl();
10043         o = o || {};
10044
10045         el.queueFx(o, function(){
10046             color = color || "ffff9c";
10047             attr = o.attr || "backgroundColor";
10048
10049             this.clearOpacity();
10050             this.show();
10051
10052             var origColor = this.getColor(attr);
10053             var restoreColor = this.dom.style[attr];
10054             endColor = (o.endColor || origColor) || "ffffff";
10055
10056             var after = function(){
10057                 el.dom.style[attr] = restoreColor;
10058                 el.afterFx(o);
10059             };
10060
10061             var a = {};
10062             a[attr] = {from: color, to: endColor};
10063             arguments.callee.anim = this.fxanim(a,
10064                 o,
10065                 'color',
10066                 1,
10067                 'easeIn', after);
10068         });
10069         return this;
10070     },
10071
10072    /**
10073     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10074     * Usage:
10075 <pre><code>
10076 // default: a single light blue ripple
10077 el.frame();
10078
10079 // custom: 3 red ripples lasting 3 seconds total
10080 el.frame("ff0000", 3, { duration: 3 });
10081
10082 // common config options shown with default values
10083 el.frame("C3DAF9", 1, {
10084     duration: 1 //duration of entire animation (not each individual ripple)
10085     // Note: Easing is not configurable and will be ignored if included
10086 });
10087 </code></pre>
10088     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10089     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10090     * @param {Object} options (optional) Object literal with any of the Fx config options
10091     * @return {Roo.Element} The Element
10092     */
10093     frame : function(color, count, o){
10094         var el = this.getFxEl();
10095         o = o || {};
10096
10097         el.queueFx(o, function(){
10098             color = color || "#C3DAF9";
10099             if(color.length == 6){
10100                 color = "#" + color;
10101             }
10102             count = count || 1;
10103             duration = o.duration || 1;
10104             this.show();
10105
10106             var b = this.getBox();
10107             var animFn = function(){
10108                 var proxy = this.createProxy({
10109
10110                      style:{
10111                         visbility:"hidden",
10112                         position:"absolute",
10113                         "z-index":"35000", // yee haw
10114                         border:"0px solid " + color
10115                      }
10116                   });
10117                 var scale = Roo.isBorderBox ? 2 : 1;
10118                 proxy.animate({
10119                     top:{from:b.y, to:b.y - 20},
10120                     left:{from:b.x, to:b.x - 20},
10121                     borderWidth:{from:0, to:10},
10122                     opacity:{from:1, to:0},
10123                     height:{from:b.height, to:(b.height + (20*scale))},
10124                     width:{from:b.width, to:(b.width + (20*scale))}
10125                 }, duration, function(){
10126                     proxy.remove();
10127                 });
10128                 if(--count > 0){
10129                      animFn.defer((duration/2)*1000, this);
10130                 }else{
10131                     el.afterFx(o);
10132                 }
10133             };
10134             animFn.call(this);
10135         });
10136         return this;
10137     },
10138
10139    /**
10140     * Creates a pause before any subsequent queued effects begin.  If there are
10141     * no effects queued after the pause it will have no effect.
10142     * Usage:
10143 <pre><code>
10144 el.pause(1);
10145 </code></pre>
10146     * @param {Number} seconds The length of time to pause (in seconds)
10147     * @return {Roo.Element} The Element
10148     */
10149     pause : function(seconds){
10150         var el = this.getFxEl();
10151         var o = {};
10152
10153         el.queueFx(o, function(){
10154             setTimeout(function(){
10155                 el.afterFx(o);
10156             }, seconds * 1000);
10157         });
10158         return this;
10159     },
10160
10161    /**
10162     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10163     * using the "endOpacity" config option.
10164     * Usage:
10165 <pre><code>
10166 // default: fade in from opacity 0 to 100%
10167 el.fadeIn();
10168
10169 // custom: fade in from opacity 0 to 75% over 2 seconds
10170 el.fadeIn({ endOpacity: .75, duration: 2});
10171
10172 // common config options shown with default values
10173 el.fadeIn({
10174     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10175     easing: 'easeOut',
10176     duration: .5
10177 });
10178 </code></pre>
10179     * @param {Object} options (optional) Object literal with any of the Fx config options
10180     * @return {Roo.Element} The Element
10181     */
10182     fadeIn : function(o){
10183         var el = this.getFxEl();
10184         o = o || {};
10185         el.queueFx(o, function(){
10186             this.setOpacity(0);
10187             this.fixDisplay();
10188             this.dom.style.visibility = 'visible';
10189             var to = o.endOpacity || 1;
10190             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10191                 o, null, .5, "easeOut", function(){
10192                 if(to == 1){
10193                     this.clearOpacity();
10194                 }
10195                 el.afterFx(o);
10196             });
10197         });
10198         return this;
10199     },
10200
10201    /**
10202     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10203     * using the "endOpacity" config option.
10204     * Usage:
10205 <pre><code>
10206 // default: fade out from the element's current opacity to 0
10207 el.fadeOut();
10208
10209 // custom: fade out from the element's current opacity to 25% over 2 seconds
10210 el.fadeOut({ endOpacity: .25, duration: 2});
10211
10212 // common config options shown with default values
10213 el.fadeOut({
10214     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10215     easing: 'easeOut',
10216     duration: .5
10217     remove: false,
10218     useDisplay: false
10219 });
10220 </code></pre>
10221     * @param {Object} options (optional) Object literal with any of the Fx config options
10222     * @return {Roo.Element} The Element
10223     */
10224     fadeOut : function(o){
10225         var el = this.getFxEl();
10226         o = o || {};
10227         el.queueFx(o, function(){
10228             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10229                 o, null, .5, "easeOut", function(){
10230                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10231                      this.dom.style.display = "none";
10232                 }else{
10233                      this.dom.style.visibility = "hidden";
10234                 }
10235                 this.clearOpacity();
10236                 el.afterFx(o);
10237             });
10238         });
10239         return this;
10240     },
10241
10242    /**
10243     * Animates the transition of an element's dimensions from a starting height/width
10244     * to an ending height/width.
10245     * Usage:
10246 <pre><code>
10247 // change height and width to 100x100 pixels
10248 el.scale(100, 100);
10249
10250 // common config options shown with default values.  The height and width will default to
10251 // the element's existing values if passed as null.
10252 el.scale(
10253     [element's width],
10254     [element's height], {
10255     easing: 'easeOut',
10256     duration: .35
10257 });
10258 </code></pre>
10259     * @param {Number} width  The new width (pass undefined to keep the original width)
10260     * @param {Number} height  The new height (pass undefined to keep the original height)
10261     * @param {Object} options (optional) Object literal with any of the Fx config options
10262     * @return {Roo.Element} The Element
10263     */
10264     scale : function(w, h, o){
10265         this.shift(Roo.apply({}, o, {
10266             width: w,
10267             height: h
10268         }));
10269         return this;
10270     },
10271
10272    /**
10273     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10274     * Any of these properties not specified in the config object will not be changed.  This effect 
10275     * requires that at least one new dimension, position or opacity setting must be passed in on
10276     * the config object in order for the function to have any effect.
10277     * Usage:
10278 <pre><code>
10279 // slide the element horizontally to x position 200 while changing the height and opacity
10280 el.shift({ x: 200, height: 50, opacity: .8 });
10281
10282 // common config options shown with default values.
10283 el.shift({
10284     width: [element's width],
10285     height: [element's height],
10286     x: [element's x position],
10287     y: [element's y position],
10288     opacity: [element's opacity],
10289     easing: 'easeOut',
10290     duration: .35
10291 });
10292 </code></pre>
10293     * @param {Object} options  Object literal with any of the Fx config options
10294     * @return {Roo.Element} The Element
10295     */
10296     shift : function(o){
10297         var el = this.getFxEl();
10298         o = o || {};
10299         el.queueFx(o, function(){
10300             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10301             if(w !== undefined){
10302                 a.width = {to: this.adjustWidth(w)};
10303             }
10304             if(h !== undefined){
10305                 a.height = {to: this.adjustHeight(h)};
10306             }
10307             if(x !== undefined || y !== undefined){
10308                 a.points = {to: [
10309                     x !== undefined ? x : this.getX(),
10310                     y !== undefined ? y : this.getY()
10311                 ]};
10312             }
10313             if(op !== undefined){
10314                 a.opacity = {to: op};
10315             }
10316             if(o.xy !== undefined){
10317                 a.points = {to: o.xy};
10318             }
10319             arguments.callee.anim = this.fxanim(a,
10320                 o, 'motion', .35, "easeOut", function(){
10321                 el.afterFx(o);
10322             });
10323         });
10324         return this;
10325     },
10326
10327         /**
10328          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10329          * ending point of the effect.
10330          * Usage:
10331          *<pre><code>
10332 // default: slide the element downward while fading out
10333 el.ghost();
10334
10335 // custom: slide the element out to the right with a 2-second duration
10336 el.ghost('r', { duration: 2 });
10337
10338 // common config options shown with default values
10339 el.ghost('b', {
10340     easing: 'easeOut',
10341     duration: .5
10342     remove: false,
10343     useDisplay: false
10344 });
10345 </code></pre>
10346          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10347          * @param {Object} options (optional) Object literal with any of the Fx config options
10348          * @return {Roo.Element} The Element
10349          */
10350     ghost : function(anchor, o){
10351         var el = this.getFxEl();
10352         o = o || {};
10353
10354         el.queueFx(o, function(){
10355             anchor = anchor || "b";
10356
10357             // restore values after effect
10358             var r = this.getFxRestore();
10359             var w = this.getWidth(),
10360                 h = this.getHeight();
10361
10362             var st = this.dom.style;
10363
10364             var after = function(){
10365                 if(o.useDisplay){
10366                     el.setDisplayed(false);
10367                 }else{
10368                     el.hide();
10369                 }
10370
10371                 el.clearOpacity();
10372                 el.setPositioning(r.pos);
10373                 st.width = r.width;
10374                 st.height = r.height;
10375
10376                 el.afterFx(o);
10377             };
10378
10379             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10380             switch(anchor.toLowerCase()){
10381                 case "t":
10382                     pt.by = [0, -h];
10383                 break;
10384                 case "l":
10385                     pt.by = [-w, 0];
10386                 break;
10387                 case "r":
10388                     pt.by = [w, 0];
10389                 break;
10390                 case "b":
10391                     pt.by = [0, h];
10392                 break;
10393                 case "tl":
10394                     pt.by = [-w, -h];
10395                 break;
10396                 case "bl":
10397                     pt.by = [-w, h];
10398                 break;
10399                 case "br":
10400                     pt.by = [w, h];
10401                 break;
10402                 case "tr":
10403                     pt.by = [w, -h];
10404                 break;
10405             }
10406
10407             arguments.callee.anim = this.fxanim(a,
10408                 o,
10409                 'motion',
10410                 .5,
10411                 "easeOut", after);
10412         });
10413         return this;
10414     },
10415
10416         /**
10417          * Ensures that all effects queued after syncFx is called on the element are
10418          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10419          * @return {Roo.Element} The Element
10420          */
10421     syncFx : function(){
10422         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10423             block : false,
10424             concurrent : true,
10425             stopFx : false
10426         });
10427         return this;
10428     },
10429
10430         /**
10431          * Ensures that all effects queued after sequenceFx is called on the element are
10432          * run in sequence.  This is the opposite of {@link #syncFx}.
10433          * @return {Roo.Element} The Element
10434          */
10435     sequenceFx : function(){
10436         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10437             block : false,
10438             concurrent : false,
10439             stopFx : false
10440         });
10441         return this;
10442     },
10443
10444         /* @private */
10445     nextFx : function(){
10446         var ef = this.fxQueue[0];
10447         if(ef){
10448             ef.call(this);
10449         }
10450     },
10451
10452         /**
10453          * Returns true if the element has any effects actively running or queued, else returns false.
10454          * @return {Boolean} True if element has active effects, else false
10455          */
10456     hasActiveFx : function(){
10457         return this.fxQueue && this.fxQueue[0];
10458     },
10459
10460         /**
10461          * Stops any running effects and clears the element's internal effects queue if it contains
10462          * any additional effects that haven't started yet.
10463          * @return {Roo.Element} The Element
10464          */
10465     stopFx : function(){
10466         if(this.hasActiveFx()){
10467             var cur = this.fxQueue[0];
10468             if(cur && cur.anim && cur.anim.isAnimated()){
10469                 this.fxQueue = [cur]; // clear out others
10470                 cur.anim.stop(true);
10471             }
10472         }
10473         return this;
10474     },
10475
10476         /* @private */
10477     beforeFx : function(o){
10478         if(this.hasActiveFx() && !o.concurrent){
10479            if(o.stopFx){
10480                this.stopFx();
10481                return true;
10482            }
10483            return false;
10484         }
10485         return true;
10486     },
10487
10488         /**
10489          * Returns true if the element is currently blocking so that no other effect can be queued
10490          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10491          * used to ensure that an effect initiated by a user action runs to completion prior to the
10492          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10493          * @return {Boolean} True if blocking, else false
10494          */
10495     hasFxBlock : function(){
10496         var q = this.fxQueue;
10497         return q && q[0] && q[0].block;
10498     },
10499
10500         /* @private */
10501     queueFx : function(o, fn){
10502         if(!this.fxQueue){
10503             this.fxQueue = [];
10504         }
10505         if(!this.hasFxBlock()){
10506             Roo.applyIf(o, this.fxDefaults);
10507             if(!o.concurrent){
10508                 var run = this.beforeFx(o);
10509                 fn.block = o.block;
10510                 this.fxQueue.push(fn);
10511                 if(run){
10512                     this.nextFx();
10513                 }
10514             }else{
10515                 fn.call(this);
10516             }
10517         }
10518         return this;
10519     },
10520
10521         /* @private */
10522     fxWrap : function(pos, o, vis){
10523         var wrap;
10524         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10525             var wrapXY;
10526             if(o.fixPosition){
10527                 wrapXY = this.getXY();
10528             }
10529             var div = document.createElement("div");
10530             div.style.visibility = vis;
10531             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10532             wrap.setPositioning(pos);
10533             if(wrap.getStyle("position") == "static"){
10534                 wrap.position("relative");
10535             }
10536             this.clearPositioning('auto');
10537             wrap.clip();
10538             wrap.dom.appendChild(this.dom);
10539             if(wrapXY){
10540                 wrap.setXY(wrapXY);
10541             }
10542         }
10543         return wrap;
10544     },
10545
10546         /* @private */
10547     fxUnwrap : function(wrap, pos, o){
10548         this.clearPositioning();
10549         this.setPositioning(pos);
10550         if(!o.wrap){
10551             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10552             wrap.remove();
10553         }
10554     },
10555
10556         /* @private */
10557     getFxRestore : function(){
10558         var st = this.dom.style;
10559         return {pos: this.getPositioning(), width: st.width, height : st.height};
10560     },
10561
10562         /* @private */
10563     afterFx : function(o){
10564         if(o.afterStyle){
10565             this.applyStyles(o.afterStyle);
10566         }
10567         if(o.afterCls){
10568             this.addClass(o.afterCls);
10569         }
10570         if(o.remove === true){
10571             this.remove();
10572         }
10573         Roo.callback(o.callback, o.scope, [this]);
10574         if(!o.concurrent){
10575             this.fxQueue.shift();
10576             this.nextFx();
10577         }
10578     },
10579
10580         /* @private */
10581     getFxEl : function(){ // support for composite element fx
10582         return Roo.get(this.dom);
10583     },
10584
10585         /* @private */
10586     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10587         animType = animType || 'run';
10588         opt = opt || {};
10589         var anim = Roo.lib.Anim[animType](
10590             this.dom, args,
10591             (opt.duration || defaultDur) || .35,
10592             (opt.easing || defaultEase) || 'easeOut',
10593             function(){
10594                 Roo.callback(cb, this);
10595             },
10596             this
10597         );
10598         opt.anim = anim;
10599         return anim;
10600     }
10601 };
10602
10603 // backwords compat
10604 Roo.Fx.resize = Roo.Fx.scale;
10605
10606 //When included, Roo.Fx is automatically applied to Element so that all basic
10607 //effects are available directly via the Element API
10608 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10609  * Based on:
10610  * Ext JS Library 1.1.1
10611  * Copyright(c) 2006-2007, Ext JS, LLC.
10612  *
10613  * Originally Released Under LGPL - original licence link has changed is not relivant.
10614  *
10615  * Fork - LGPL
10616  * <script type="text/javascript">
10617  */
10618
10619
10620 /**
10621  * @class Roo.CompositeElement
10622  * Standard composite class. Creates a Roo.Element for every element in the collection.
10623  * <br><br>
10624  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10625  * actions will be performed on all the elements in this collection.</b>
10626  * <br><br>
10627  * All methods return <i>this</i> and can be chained.
10628  <pre><code>
10629  var els = Roo.select("#some-el div.some-class", true);
10630  // or select directly from an existing element
10631  var el = Roo.get('some-el');
10632  el.select('div.some-class', true);
10633
10634  els.setWidth(100); // all elements become 100 width
10635  els.hide(true); // all elements fade out and hide
10636  // or
10637  els.setWidth(100).hide(true);
10638  </code></pre>
10639  */
10640 Roo.CompositeElement = function(els){
10641     this.elements = [];
10642     this.addElements(els);
10643 };
10644 Roo.CompositeElement.prototype = {
10645     isComposite: true,
10646     addElements : function(els){
10647         if(!els) return this;
10648         if(typeof els == "string"){
10649             els = Roo.Element.selectorFunction(els);
10650         }
10651         var yels = this.elements;
10652         var index = yels.length-1;
10653         for(var i = 0, len = els.length; i < len; i++) {
10654                 yels[++index] = Roo.get(els[i]);
10655         }
10656         return this;
10657     },
10658
10659     /**
10660     * Clears this composite and adds the elements returned by the passed selector.
10661     * @param {String/Array} els A string CSS selector, an array of elements or an element
10662     * @return {CompositeElement} this
10663     */
10664     fill : function(els){
10665         this.elements = [];
10666         this.add(els);
10667         return this;
10668     },
10669
10670     /**
10671     * Filters this composite to only elements that match the passed selector.
10672     * @param {String} selector A string CSS selector
10673     * @return {CompositeElement} this
10674     */
10675     filter : function(selector){
10676         var els = [];
10677         this.each(function(el){
10678             if(el.is(selector)){
10679                 els[els.length] = el.dom;
10680             }
10681         });
10682         this.fill(els);
10683         return this;
10684     },
10685
10686     invoke : function(fn, args){
10687         var els = this.elements;
10688         for(var i = 0, len = els.length; i < len; i++) {
10689                 Roo.Element.prototype[fn].apply(els[i], args);
10690         }
10691         return this;
10692     },
10693     /**
10694     * Adds elements to this composite.
10695     * @param {String/Array} els A string CSS selector, an array of elements or an element
10696     * @return {CompositeElement} this
10697     */
10698     add : function(els){
10699         if(typeof els == "string"){
10700             this.addElements(Roo.Element.selectorFunction(els));
10701         }else if(els.length !== undefined){
10702             this.addElements(els);
10703         }else{
10704             this.addElements([els]);
10705         }
10706         return this;
10707     },
10708     /**
10709     * Calls the passed function passing (el, this, index) for each element in this composite.
10710     * @param {Function} fn The function to call
10711     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10712     * @return {CompositeElement} this
10713     */
10714     each : function(fn, scope){
10715         var els = this.elements;
10716         for(var i = 0, len = els.length; i < len; i++){
10717             if(fn.call(scope || els[i], els[i], this, i) === false) {
10718                 break;
10719             }
10720         }
10721         return this;
10722     },
10723
10724     /**
10725      * Returns the Element object at the specified index
10726      * @param {Number} index
10727      * @return {Roo.Element}
10728      */
10729     item : function(index){
10730         return this.elements[index] || null;
10731     },
10732
10733     /**
10734      * Returns the first Element
10735      * @return {Roo.Element}
10736      */
10737     first : function(){
10738         return this.item(0);
10739     },
10740
10741     /**
10742      * Returns the last Element
10743      * @return {Roo.Element}
10744      */
10745     last : function(){
10746         return this.item(this.elements.length-1);
10747     },
10748
10749     /**
10750      * Returns the number of elements in this composite
10751      * @return Number
10752      */
10753     getCount : function(){
10754         return this.elements.length;
10755     },
10756
10757     /**
10758      * Returns true if this composite contains the passed element
10759      * @return Boolean
10760      */
10761     contains : function(el){
10762         return this.indexOf(el) !== -1;
10763     },
10764
10765     /**
10766      * Returns true if this composite contains the passed element
10767      * @return Boolean
10768      */
10769     indexOf : function(el){
10770         return this.elements.indexOf(Roo.get(el));
10771     },
10772
10773
10774     /**
10775     * Removes the specified element(s).
10776     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10777     * or an array of any of those.
10778     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10779     * @return {CompositeElement} this
10780     */
10781     removeElement : function(el, removeDom){
10782         if(el instanceof Array){
10783             for(var i = 0, len = el.length; i < len; i++){
10784                 this.removeElement(el[i]);
10785             }
10786             return this;
10787         }
10788         var index = typeof el == 'number' ? el : this.indexOf(el);
10789         if(index !== -1){
10790             if(removeDom){
10791                 var d = this.elements[index];
10792                 if(d.dom){
10793                     d.remove();
10794                 }else{
10795                     d.parentNode.removeChild(d);
10796                 }
10797             }
10798             this.elements.splice(index, 1);
10799         }
10800         return this;
10801     },
10802
10803     /**
10804     * Replaces the specified element with the passed element.
10805     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10806     * to replace.
10807     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10808     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10809     * @return {CompositeElement} this
10810     */
10811     replaceElement : function(el, replacement, domReplace){
10812         var index = typeof el == 'number' ? el : this.indexOf(el);
10813         if(index !== -1){
10814             if(domReplace){
10815                 this.elements[index].replaceWith(replacement);
10816             }else{
10817                 this.elements.splice(index, 1, Roo.get(replacement))
10818             }
10819         }
10820         return this;
10821     },
10822
10823     /**
10824      * Removes all elements.
10825      */
10826     clear : function(){
10827         this.elements = [];
10828     }
10829 };
10830 (function(){
10831     Roo.CompositeElement.createCall = function(proto, fnName){
10832         if(!proto[fnName]){
10833             proto[fnName] = function(){
10834                 return this.invoke(fnName, arguments);
10835             };
10836         }
10837     };
10838     for(var fnName in Roo.Element.prototype){
10839         if(typeof Roo.Element.prototype[fnName] == "function"){
10840             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10841         }
10842     };
10843 })();
10844 /*
10845  * Based on:
10846  * Ext JS Library 1.1.1
10847  * Copyright(c) 2006-2007, Ext JS, LLC.
10848  *
10849  * Originally Released Under LGPL - original licence link has changed is not relivant.
10850  *
10851  * Fork - LGPL
10852  * <script type="text/javascript">
10853  */
10854
10855 /**
10856  * @class Roo.CompositeElementLite
10857  * @extends Roo.CompositeElement
10858  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10859  <pre><code>
10860  var els = Roo.select("#some-el div.some-class");
10861  // or select directly from an existing element
10862  var el = Roo.get('some-el');
10863  el.select('div.some-class');
10864
10865  els.setWidth(100); // all elements become 100 width
10866  els.hide(true); // all elements fade out and hide
10867  // or
10868  els.setWidth(100).hide(true);
10869  </code></pre><br><br>
10870  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10871  * actions will be performed on all the elements in this collection.</b>
10872  */
10873 Roo.CompositeElementLite = function(els){
10874     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10875     this.el = new Roo.Element.Flyweight();
10876 };
10877 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10878     addElements : function(els){
10879         if(els){
10880             if(els instanceof Array){
10881                 this.elements = this.elements.concat(els);
10882             }else{
10883                 var yels = this.elements;
10884                 var index = yels.length-1;
10885                 for(var i = 0, len = els.length; i < len; i++) {
10886                     yels[++index] = els[i];
10887                 }
10888             }
10889         }
10890         return this;
10891     },
10892     invoke : function(fn, args){
10893         var els = this.elements;
10894         var el = this.el;
10895         for(var i = 0, len = els.length; i < len; i++) {
10896             el.dom = els[i];
10897                 Roo.Element.prototype[fn].apply(el, args);
10898         }
10899         return this;
10900     },
10901     /**
10902      * Returns a flyweight Element of the dom element object at the specified index
10903      * @param {Number} index
10904      * @return {Roo.Element}
10905      */
10906     item : function(index){
10907         if(!this.elements[index]){
10908             return null;
10909         }
10910         this.el.dom = this.elements[index];
10911         return this.el;
10912     },
10913
10914     // fixes scope with flyweight
10915     addListener : function(eventName, handler, scope, opt){
10916         var els = this.elements;
10917         for(var i = 0, len = els.length; i < len; i++) {
10918             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10919         }
10920         return this;
10921     },
10922
10923     /**
10924     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10925     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10926     * a reference to the dom node, use el.dom.</b>
10927     * @param {Function} fn The function to call
10928     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10929     * @return {CompositeElement} this
10930     */
10931     each : function(fn, scope){
10932         var els = this.elements;
10933         var el = this.el;
10934         for(var i = 0, len = els.length; i < len; i++){
10935             el.dom = els[i];
10936                 if(fn.call(scope || el, el, this, i) === false){
10937                 break;
10938             }
10939         }
10940         return this;
10941     },
10942
10943     indexOf : function(el){
10944         return this.elements.indexOf(Roo.getDom(el));
10945     },
10946
10947     replaceElement : function(el, replacement, domReplace){
10948         var index = typeof el == 'number' ? el : this.indexOf(el);
10949         if(index !== -1){
10950             replacement = Roo.getDom(replacement);
10951             if(domReplace){
10952                 var d = this.elements[index];
10953                 d.parentNode.insertBefore(replacement, d);
10954                 d.parentNode.removeChild(d);
10955             }
10956             this.elements.splice(index, 1, replacement);
10957         }
10958         return this;
10959     }
10960 });
10961 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10962
10963 /*
10964  * Based on:
10965  * Ext JS Library 1.1.1
10966  * Copyright(c) 2006-2007, Ext JS, LLC.
10967  *
10968  * Originally Released Under LGPL - original licence link has changed is not relivant.
10969  *
10970  * Fork - LGPL
10971  * <script type="text/javascript">
10972  */
10973
10974  
10975
10976 /**
10977  * @class Roo.data.Connection
10978  * @extends Roo.util.Observable
10979  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10980  * either to a configured URL, or to a URL specified at request time.<br><br>
10981  * <p>
10982  * Requests made by this class are asynchronous, and will return immediately. No data from
10983  * the server will be available to the statement immediately following the {@link #request} call.
10984  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10985  * <p>
10986  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10987  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10988  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10989  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10990  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10991  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10992  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10993  * standard DOM methods.
10994  * @constructor
10995  * @param {Object} config a configuration object.
10996  */
10997 Roo.data.Connection = function(config){
10998     Roo.apply(this, config);
10999     this.addEvents({
11000         /**
11001          * @event beforerequest
11002          * Fires before a network request is made to retrieve a data object.
11003          * @param {Connection} conn This Connection object.
11004          * @param {Object} options The options config object passed to the {@link #request} method.
11005          */
11006         "beforerequest" : true,
11007         /**
11008          * @event requestcomplete
11009          * Fires if the request was successfully completed.
11010          * @param {Connection} conn This Connection object.
11011          * @param {Object} response The XHR object containing the response data.
11012          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11013          * @param {Object} options The options config object passed to the {@link #request} method.
11014          */
11015         "requestcomplete" : true,
11016         /**
11017          * @event requestexception
11018          * Fires if an error HTTP status was returned from the server.
11019          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11020          * @param {Connection} conn This Connection object.
11021          * @param {Object} response The XHR object containing the response data.
11022          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11023          * @param {Object} options The options config object passed to the {@link #request} method.
11024          */
11025         "requestexception" : true
11026     });
11027     Roo.data.Connection.superclass.constructor.call(this);
11028 };
11029
11030 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11031     /**
11032      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11033      */
11034     /**
11035      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11036      * extra parameters to each request made by this object. (defaults to undefined)
11037      */
11038     /**
11039      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11040      *  to each request made by this object. (defaults to undefined)
11041      */
11042     /**
11043      * @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)
11044      */
11045     /**
11046      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11047      */
11048     timeout : 30000,
11049     /**
11050      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11051      * @type Boolean
11052      */
11053     autoAbort:false,
11054
11055     /**
11056      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11057      * @type Boolean
11058      */
11059     disableCaching: true,
11060
11061     /**
11062      * Sends an HTTP request to a remote server.
11063      * @param {Object} options An object which may contain the following properties:<ul>
11064      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11065      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11066      * request, a url encoded string or a function to call to get either.</li>
11067      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11068      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11069      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11070      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11071      * <li>options {Object} The parameter to the request call.</li>
11072      * <li>success {Boolean} True if the request succeeded.</li>
11073      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11074      * </ul></li>
11075      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11076      * The callback is passed the following parameters:<ul>
11077      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11078      * <li>options {Object} The parameter to the request call.</li>
11079      * </ul></li>
11080      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11081      * The callback is passed the following parameters:<ul>
11082      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11083      * <li>options {Object} The parameter to the request call.</li>
11084      * </ul></li>
11085      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11086      * for the callback function. Defaults to the browser window.</li>
11087      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11088      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11089      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11090      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11091      * params for the post data. Any params will be appended to the URL.</li>
11092      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11093      * </ul>
11094      * @return {Number} transactionId
11095      */
11096     request : function(o){
11097         if(this.fireEvent("beforerequest", this, o) !== false){
11098             var p = o.params;
11099
11100             if(typeof p == "function"){
11101                 p = p.call(o.scope||window, o);
11102             }
11103             if(typeof p == "object"){
11104                 p = Roo.urlEncode(o.params);
11105             }
11106             if(this.extraParams){
11107                 var extras = Roo.urlEncode(this.extraParams);
11108                 p = p ? (p + '&' + extras) : extras;
11109             }
11110
11111             var url = o.url || this.url;
11112             if(typeof url == 'function'){
11113                 url = url.call(o.scope||window, o);
11114             }
11115
11116             if(o.form){
11117                 var form = Roo.getDom(o.form);
11118                 url = url || form.action;
11119
11120                 var enctype = form.getAttribute("enctype");
11121                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11122                     return this.doFormUpload(o, p, url);
11123                 }
11124                 var f = Roo.lib.Ajax.serializeForm(form);
11125                 p = p ? (p + '&' + f) : f;
11126             }
11127
11128             var hs = o.headers;
11129             if(this.defaultHeaders){
11130                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11131                 if(!o.headers){
11132                     o.headers = hs;
11133                 }
11134             }
11135
11136             var cb = {
11137                 success: this.handleResponse,
11138                 failure: this.handleFailure,
11139                 scope: this,
11140                 argument: {options: o},
11141                 timeout : this.timeout
11142             };
11143
11144             var method = o.method||this.method||(p ? "POST" : "GET");
11145
11146             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11147                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11148             }
11149
11150             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11151                 if(o.autoAbort){
11152                     this.abort();
11153                 }
11154             }else if(this.autoAbort !== false){
11155                 this.abort();
11156             }
11157
11158             if((method == 'GET' && p) || o.xmlData){
11159                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11160                 p = '';
11161             }
11162             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11163             return this.transId;
11164         }else{
11165             Roo.callback(o.callback, o.scope, [o, null, null]);
11166             return null;
11167         }
11168     },
11169
11170     /**
11171      * Determine whether this object has a request outstanding.
11172      * @param {Number} transactionId (Optional) defaults to the last transaction
11173      * @return {Boolean} True if there is an outstanding request.
11174      */
11175     isLoading : function(transId){
11176         if(transId){
11177             return Roo.lib.Ajax.isCallInProgress(transId);
11178         }else{
11179             return this.transId ? true : false;
11180         }
11181     },
11182
11183     /**
11184      * Aborts any outstanding request.
11185      * @param {Number} transactionId (Optional) defaults to the last transaction
11186      */
11187     abort : function(transId){
11188         if(transId || this.isLoading()){
11189             Roo.lib.Ajax.abort(transId || this.transId);
11190         }
11191     },
11192
11193     // private
11194     handleResponse : function(response){
11195         this.transId = false;
11196         var options = response.argument.options;
11197         response.argument = options ? options.argument : null;
11198         this.fireEvent("requestcomplete", this, response, options);
11199         Roo.callback(options.success, options.scope, [response, options]);
11200         Roo.callback(options.callback, options.scope, [options, true, response]);
11201     },
11202
11203     // private
11204     handleFailure : function(response, e){
11205         this.transId = false;
11206         var options = response.argument.options;
11207         response.argument = options ? options.argument : null;
11208         this.fireEvent("requestexception", this, response, options, e);
11209         Roo.callback(options.failure, options.scope, [response, options]);
11210         Roo.callback(options.callback, options.scope, [options, false, response]);
11211     },
11212
11213     // private
11214     doFormUpload : function(o, ps, url){
11215         var id = Roo.id();
11216         var frame = document.createElement('iframe');
11217         frame.id = id;
11218         frame.name = id;
11219         frame.className = 'x-hidden';
11220         if(Roo.isIE){
11221             frame.src = Roo.SSL_SECURE_URL;
11222         }
11223         document.body.appendChild(frame);
11224
11225         if(Roo.isIE){
11226            document.frames[id].name = id;
11227         }
11228
11229         var form = Roo.getDom(o.form);
11230         form.target = id;
11231         form.method = 'POST';
11232         form.enctype = form.encoding = 'multipart/form-data';
11233         if(url){
11234             form.action = url;
11235         }
11236
11237         var hiddens, hd;
11238         if(ps){ // add dynamic params
11239             hiddens = [];
11240             ps = Roo.urlDecode(ps, false);
11241             for(var k in ps){
11242                 if(ps.hasOwnProperty(k)){
11243                     hd = document.createElement('input');
11244                     hd.type = 'hidden';
11245                     hd.name = k;
11246                     hd.value = ps[k];
11247                     form.appendChild(hd);
11248                     hiddens.push(hd);
11249                 }
11250             }
11251         }
11252
11253         function cb(){
11254             var r = {  // bogus response object
11255                 responseText : '',
11256                 responseXML : null
11257             };
11258
11259             r.argument = o ? o.argument : null;
11260
11261             try { //
11262                 var doc;
11263                 if(Roo.isIE){
11264                     doc = frame.contentWindow.document;
11265                 }else {
11266                     doc = (frame.contentDocument || window.frames[id].document);
11267                 }
11268                 if(doc && doc.body){
11269                     r.responseText = doc.body.innerHTML;
11270                 }
11271                 if(doc && doc.XMLDocument){
11272                     r.responseXML = doc.XMLDocument;
11273                 }else {
11274                     r.responseXML = doc;
11275                 }
11276             }
11277             catch(e) {
11278                 // ignore
11279             }
11280
11281             Roo.EventManager.removeListener(frame, 'load', cb, this);
11282
11283             this.fireEvent("requestcomplete", this, r, o);
11284             Roo.callback(o.success, o.scope, [r, o]);
11285             Roo.callback(o.callback, o.scope, [o, true, r]);
11286
11287             setTimeout(function(){document.body.removeChild(frame);}, 100);
11288         }
11289
11290         Roo.EventManager.on(frame, 'load', cb, this);
11291         form.submit();
11292
11293         if(hiddens){ // remove dynamic params
11294             for(var i = 0, len = hiddens.length; i < len; i++){
11295                 form.removeChild(hiddens[i]);
11296             }
11297         }
11298     }
11299 });
11300
11301 /**
11302  * @class Roo.Ajax
11303  * @extends Roo.data.Connection
11304  * Global Ajax request class.
11305  *
11306  * @singleton
11307  */
11308 Roo.Ajax = new Roo.data.Connection({
11309     // fix up the docs
11310    /**
11311      * @cfg {String} url @hide
11312      */
11313     /**
11314      * @cfg {Object} extraParams @hide
11315      */
11316     /**
11317      * @cfg {Object} defaultHeaders @hide
11318      */
11319     /**
11320      * @cfg {String} method (Optional) @hide
11321      */
11322     /**
11323      * @cfg {Number} timeout (Optional) @hide
11324      */
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) @hide
11327      */
11328
11329     /**
11330      * @cfg {Boolean} disableCaching (Optional) @hide
11331      */
11332
11333     /**
11334      * @property  disableCaching
11335      * True to add a unique cache-buster param to GET requests. (defaults to true)
11336      * @type Boolean
11337      */
11338     /**
11339      * @property  url
11340      * The default URL to be used for requests to the server. (defaults to undefined)
11341      * @type String
11342      */
11343     /**
11344      * @property  extraParams
11345      * An object containing properties which are used as
11346      * extra parameters to each request made by this object. (defaults to undefined)
11347      * @type Object
11348      */
11349     /**
11350      * @property  defaultHeaders
11351      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11352      * @type Object
11353      */
11354     /**
11355      * @property  method
11356      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11357      * @type String
11358      */
11359     /**
11360      * @property  timeout
11361      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11362      * @type Number
11363      */
11364
11365     /**
11366      * @property  autoAbort
11367      * Whether a new request should abort any pending requests. (defaults to false)
11368      * @type Boolean
11369      */
11370     autoAbort : false,
11371
11372     /**
11373      * Serialize the passed form into a url encoded string
11374      * @param {String/HTMLElement} form
11375      * @return {String}
11376      */
11377     serializeForm : function(form){
11378         return Roo.lib.Ajax.serializeForm(form);
11379     }
11380 });/*
11381  * Based on:
11382  * Ext JS Library 1.1.1
11383  * Copyright(c) 2006-2007, Ext JS, LLC.
11384  *
11385  * Originally Released Under LGPL - original licence link has changed is not relivant.
11386  *
11387  * Fork - LGPL
11388  * <script type="text/javascript">
11389  */
11390  
11391 /**
11392  * @class Roo.Ajax
11393  * @extends Roo.data.Connection
11394  * Global Ajax request class.
11395  *
11396  * @instanceOf  Roo.data.Connection
11397  */
11398 Roo.Ajax = new Roo.data.Connection({
11399     // fix up the docs
11400     
11401     /**
11402      * fix up scoping
11403      * @scope Roo.Ajax
11404      */
11405     
11406    /**
11407      * @cfg {String} url @hide
11408      */
11409     /**
11410      * @cfg {Object} extraParams @hide
11411      */
11412     /**
11413      * @cfg {Object} defaultHeaders @hide
11414      */
11415     /**
11416      * @cfg {String} method (Optional) @hide
11417      */
11418     /**
11419      * @cfg {Number} timeout (Optional) @hide
11420      */
11421     /**
11422      * @cfg {Boolean} autoAbort (Optional) @hide
11423      */
11424
11425     /**
11426      * @cfg {Boolean} disableCaching (Optional) @hide
11427      */
11428
11429     /**
11430      * @property  disableCaching
11431      * True to add a unique cache-buster param to GET requests. (defaults to true)
11432      * @type Boolean
11433      */
11434     /**
11435      * @property  url
11436      * The default URL to be used for requests to the server. (defaults to undefined)
11437      * @type String
11438      */
11439     /**
11440      * @property  extraParams
11441      * An object containing properties which are used as
11442      * extra parameters to each request made by this object. (defaults to undefined)
11443      * @type Object
11444      */
11445     /**
11446      * @property  defaultHeaders
11447      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11448      * @type Object
11449      */
11450     /**
11451      * @property  method
11452      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11453      * @type String
11454      */
11455     /**
11456      * @property  timeout
11457      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11458      * @type Number
11459      */
11460
11461     /**
11462      * @property  autoAbort
11463      * Whether a new request should abort any pending requests. (defaults to false)
11464      * @type Boolean
11465      */
11466     autoAbort : false,
11467
11468     /**
11469      * Serialize the passed form into a url encoded string
11470      * @param {String/HTMLElement} form
11471      * @return {String}
11472      */
11473     serializeForm : function(form){
11474         return Roo.lib.Ajax.serializeForm(form);
11475     }
11476 });/*
11477  * Based on:
11478  * Ext JS Library 1.1.1
11479  * Copyright(c) 2006-2007, Ext JS, LLC.
11480  *
11481  * Originally Released Under LGPL - original licence link has changed is not relivant.
11482  *
11483  * Fork - LGPL
11484  * <script type="text/javascript">
11485  */
11486
11487  
11488 /**
11489  * @class Roo.UpdateManager
11490  * @extends Roo.util.Observable
11491  * Provides AJAX-style update for Element object.<br><br>
11492  * Usage:<br>
11493  * <pre><code>
11494  * // Get it from a Roo.Element object
11495  * var el = Roo.get("foo");
11496  * var mgr = el.getUpdateManager();
11497  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11498  * ...
11499  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11500  * <br>
11501  * // or directly (returns the same UpdateManager instance)
11502  * var mgr = new Roo.UpdateManager("myElementId");
11503  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11504  * mgr.on("update", myFcnNeedsToKnow);
11505  * <br>
11506    // short handed call directly from the element object
11507    Roo.get("foo").load({
11508         url: "bar.php",
11509         scripts:true,
11510         params: "for=bar",
11511         text: "Loading Foo..."
11512    });
11513  * </code></pre>
11514  * @constructor
11515  * Create new UpdateManager directly.
11516  * @param {String/HTMLElement/Roo.Element} el The element to update
11517  * @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).
11518  */
11519 Roo.UpdateManager = function(el, forceNew){
11520     el = Roo.get(el);
11521     if(!forceNew && el.updateManager){
11522         return el.updateManager;
11523     }
11524     /**
11525      * The Element object
11526      * @type Roo.Element
11527      */
11528     this.el = el;
11529     /**
11530      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11531      * @type String
11532      */
11533     this.defaultUrl = null;
11534
11535     this.addEvents({
11536         /**
11537          * @event beforeupdate
11538          * Fired before an update is made, return false from your handler and the update is cancelled.
11539          * @param {Roo.Element} el
11540          * @param {String/Object/Function} url
11541          * @param {String/Object} params
11542          */
11543         "beforeupdate": true,
11544         /**
11545          * @event update
11546          * Fired after successful update is made.
11547          * @param {Roo.Element} el
11548          * @param {Object} oResponseObject The response Object
11549          */
11550         "update": true,
11551         /**
11552          * @event failure
11553          * Fired on update failure.
11554          * @param {Roo.Element} el
11555          * @param {Object} oResponseObject The response Object
11556          */
11557         "failure": true
11558     });
11559     var d = Roo.UpdateManager.defaults;
11560     /**
11561      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11562      * @type String
11563      */
11564     this.sslBlankUrl = d.sslBlankUrl;
11565     /**
11566      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11567      * @type Boolean
11568      */
11569     this.disableCaching = d.disableCaching;
11570     /**
11571      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11572      * @type String
11573      */
11574     this.indicatorText = d.indicatorText;
11575     /**
11576      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11577      * @type String
11578      */
11579     this.showLoadIndicator = d.showLoadIndicator;
11580     /**
11581      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11582      * @type Number
11583      */
11584     this.timeout = d.timeout;
11585
11586     /**
11587      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11588      * @type Boolean
11589      */
11590     this.loadScripts = d.loadScripts;
11591
11592     /**
11593      * Transaction object of current executing transaction
11594      */
11595     this.transaction = null;
11596
11597     /**
11598      * @private
11599      */
11600     this.autoRefreshProcId = null;
11601     /**
11602      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11603      * @type Function
11604      */
11605     this.refreshDelegate = this.refresh.createDelegate(this);
11606     /**
11607      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11608      * @type Function
11609      */
11610     this.updateDelegate = this.update.createDelegate(this);
11611     /**
11612      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11613      * @type Function
11614      */
11615     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11616     /**
11617      * @private
11618      */
11619     this.successDelegate = this.processSuccess.createDelegate(this);
11620     /**
11621      * @private
11622      */
11623     this.failureDelegate = this.processFailure.createDelegate(this);
11624
11625     if(!this.renderer){
11626      /**
11627       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11628       */
11629     this.renderer = new Roo.UpdateManager.BasicRenderer();
11630     }
11631     
11632     Roo.UpdateManager.superclass.constructor.call(this);
11633 };
11634
11635 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11636     /**
11637      * Get the Element this UpdateManager is bound to
11638      * @return {Roo.Element} The element
11639      */
11640     getEl : function(){
11641         return this.el;
11642     },
11643     /**
11644      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11645      * @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:
11646 <pre><code>
11647 um.update({<br/>
11648     url: "your-url.php",<br/>
11649     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11650     callback: yourFunction,<br/>
11651     scope: yourObject, //(optional scope)  <br/>
11652     discardUrl: false, <br/>
11653     nocache: false,<br/>
11654     text: "Loading...",<br/>
11655     timeout: 30,<br/>
11656     scripts: false<br/>
11657 });
11658 </code></pre>
11659      * The only required property is url. The optional properties nocache, text and scripts
11660      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11661      * @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}
11662      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11663      * @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.
11664      */
11665     update : function(url, params, callback, discardUrl){
11666         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11667             var method = this.method, cfg;
11668             if(typeof url == "object"){ // must be config object
11669                 cfg = url;
11670                 url = cfg.url;
11671                 params = params || cfg.params;
11672                 callback = callback || cfg.callback;
11673                 discardUrl = discardUrl || cfg.discardUrl;
11674                 if(callback && cfg.scope){
11675                     callback = callback.createDelegate(cfg.scope);
11676                 }
11677                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11678                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11679                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11680                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11681                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11682             }
11683             this.showLoading();
11684             if(!discardUrl){
11685                 this.defaultUrl = url;
11686             }
11687             if(typeof url == "function"){
11688                 url = url.call(this);
11689             }
11690
11691             method = method || (params ? "POST" : "GET");
11692             if(method == "GET"){
11693                 url = this.prepareUrl(url);
11694             }
11695
11696             var o = Roo.apply(cfg ||{}, {
11697                 url : url,
11698                 params: params,
11699                 success: this.successDelegate,
11700                 failure: this.failureDelegate,
11701                 callback: undefined,
11702                 timeout: (this.timeout*1000),
11703                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11704             });
11705
11706             this.transaction = Roo.Ajax.request(o);
11707         }
11708     },
11709
11710     /**
11711      * 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.
11712      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11713      * @param {String/HTMLElement} form The form Id or form element
11714      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11715      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11716      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11717      */
11718     formUpdate : function(form, url, reset, callback){
11719         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11720             if(typeof url == "function"){
11721                 url = url.call(this);
11722             }
11723             form = Roo.getDom(form);
11724             this.transaction = Roo.Ajax.request({
11725                 form: form,
11726                 url:url,
11727                 success: this.successDelegate,
11728                 failure: this.failureDelegate,
11729                 timeout: (this.timeout*1000),
11730                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11731             });
11732             this.showLoading.defer(1, this);
11733         }
11734     },
11735
11736     /**
11737      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11738      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11739      */
11740     refresh : function(callback){
11741         if(this.defaultUrl == null){
11742             return;
11743         }
11744         this.update(this.defaultUrl, null, callback, true);
11745     },
11746
11747     /**
11748      * Set this element to auto refresh.
11749      * @param {Number} interval How often to update (in seconds).
11750      * @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)
11751      * @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}
11752      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11753      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11754      */
11755     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11756         if(refreshNow){
11757             this.update(url || this.defaultUrl, params, callback, true);
11758         }
11759         if(this.autoRefreshProcId){
11760             clearInterval(this.autoRefreshProcId);
11761         }
11762         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11763     },
11764
11765     /**
11766      * Stop auto refresh on this element.
11767      */
11768      stopAutoRefresh : function(){
11769         if(this.autoRefreshProcId){
11770             clearInterval(this.autoRefreshProcId);
11771             delete this.autoRefreshProcId;
11772         }
11773     },
11774
11775     isAutoRefreshing : function(){
11776        return this.autoRefreshProcId ? true : false;
11777     },
11778     /**
11779      * Called to update the element to "Loading" state. Override to perform custom action.
11780      */
11781     showLoading : function(){
11782         if(this.showLoadIndicator){
11783             this.el.update(this.indicatorText);
11784         }
11785     },
11786
11787     /**
11788      * Adds unique parameter to query string if disableCaching = true
11789      * @private
11790      */
11791     prepareUrl : function(url){
11792         if(this.disableCaching){
11793             var append = "_dc=" + (new Date().getTime());
11794             if(url.indexOf("?") !== -1){
11795                 url += "&" + append;
11796             }else{
11797                 url += "?" + append;
11798             }
11799         }
11800         return url;
11801     },
11802
11803     /**
11804      * @private
11805      */
11806     processSuccess : function(response){
11807         this.transaction = null;
11808         if(response.argument.form && response.argument.reset){
11809             try{ // put in try/catch since some older FF releases had problems with this
11810                 response.argument.form.reset();
11811             }catch(e){}
11812         }
11813         if(this.loadScripts){
11814             this.renderer.render(this.el, response, this,
11815                 this.updateComplete.createDelegate(this, [response]));
11816         }else{
11817             this.renderer.render(this.el, response, this);
11818             this.updateComplete(response);
11819         }
11820     },
11821
11822     updateComplete : function(response){
11823         this.fireEvent("update", this.el, response);
11824         if(typeof response.argument.callback == "function"){
11825             response.argument.callback(this.el, true, response);
11826         }
11827     },
11828
11829     /**
11830      * @private
11831      */
11832     processFailure : function(response){
11833         this.transaction = null;
11834         this.fireEvent("failure", this.el, response);
11835         if(typeof response.argument.callback == "function"){
11836             response.argument.callback(this.el, false, response);
11837         }
11838     },
11839
11840     /**
11841      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11842      * @param {Object} renderer The object implementing the render() method
11843      */
11844     setRenderer : function(renderer){
11845         this.renderer = renderer;
11846     },
11847
11848     getRenderer : function(){
11849        return this.renderer;
11850     },
11851
11852     /**
11853      * Set the defaultUrl used for updates
11854      * @param {String/Function} defaultUrl The url or a function to call to get the url
11855      */
11856     setDefaultUrl : function(defaultUrl){
11857         this.defaultUrl = defaultUrl;
11858     },
11859
11860     /**
11861      * Aborts the executing transaction
11862      */
11863     abort : function(){
11864         if(this.transaction){
11865             Roo.Ajax.abort(this.transaction);
11866         }
11867     },
11868
11869     /**
11870      * Returns true if an update is in progress
11871      * @return {Boolean}
11872      */
11873     isUpdating : function(){
11874         if(this.transaction){
11875             return Roo.Ajax.isLoading(this.transaction);
11876         }
11877         return false;
11878     }
11879 });
11880
11881 /**
11882  * @class Roo.UpdateManager.defaults
11883  * @static (not really - but it helps the doc tool)
11884  * The defaults collection enables customizing the default properties of UpdateManager
11885  */
11886    Roo.UpdateManager.defaults = {
11887        /**
11888          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11889          * @type Number
11890          */
11891          timeout : 30,
11892
11893          /**
11894          * True to process scripts by default (Defaults to false).
11895          * @type Boolean
11896          */
11897         loadScripts : false,
11898
11899         /**
11900         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11901         * @type String
11902         */
11903         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11904         /**
11905          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11906          * @type Boolean
11907          */
11908         disableCaching : false,
11909         /**
11910          * Whether to show indicatorText when loading (Defaults to true).
11911          * @type Boolean
11912          */
11913         showLoadIndicator : true,
11914         /**
11915          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11916          * @type String
11917          */
11918         indicatorText : '<div class="loading-indicator">Loading...</div>'
11919    };
11920
11921 /**
11922  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11923  *Usage:
11924  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11925  * @param {String/HTMLElement/Roo.Element} el The element to update
11926  * @param {String} url The url
11927  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11928  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11929  * @static
11930  * @deprecated
11931  * @member Roo.UpdateManager
11932  */
11933 Roo.UpdateManager.updateElement = function(el, url, params, options){
11934     var um = Roo.get(el, true).getUpdateManager();
11935     Roo.apply(um, options);
11936     um.update(url, params, options ? options.callback : null);
11937 };
11938 // alias for backwards compat
11939 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11940 /**
11941  * @class Roo.UpdateManager.BasicRenderer
11942  * Default Content renderer. Updates the elements innerHTML with the responseText.
11943  */
11944 Roo.UpdateManager.BasicRenderer = function(){};
11945
11946 Roo.UpdateManager.BasicRenderer.prototype = {
11947     /**
11948      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11949      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11950      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11951      * @param {Roo.Element} el The element being rendered
11952      * @param {Object} response The YUI Connect response object
11953      * @param {UpdateManager} updateManager The calling update manager
11954      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11955      */
11956      render : function(el, response, updateManager, callback){
11957         el.update(response.responseText, updateManager.loadScripts, callback);
11958     }
11959 };
11960 /*
11961  * Based on:
11962  * Ext JS Library 1.1.1
11963  * Copyright(c) 2006-2007, Ext JS, LLC.
11964  *
11965  * Originally Released Under LGPL - original licence link has changed is not relivant.
11966  *
11967  * Fork - LGPL
11968  * <script type="text/javascript">
11969  */
11970
11971 /**
11972  * @class Roo.util.DelayedTask
11973  * Provides a convenient method of performing setTimeout where a new
11974  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11975  * You can use this class to buffer
11976  * the keypress events for a certain number of milliseconds, and perform only if they stop
11977  * for that amount of time.
11978  * @constructor The parameters to this constructor serve as defaults and are not required.
11979  * @param {Function} fn (optional) The default function to timeout
11980  * @param {Object} scope (optional) The default scope of that timeout
11981  * @param {Array} args (optional) The default Array of arguments
11982  */
11983 Roo.util.DelayedTask = function(fn, scope, args){
11984     var id = null, d, t;
11985
11986     var call = function(){
11987         var now = new Date().getTime();
11988         if(now - t >= d){
11989             clearInterval(id);
11990             id = null;
11991             fn.apply(scope, args || []);
11992         }
11993     };
11994     /**
11995      * Cancels any pending timeout and queues a new one
11996      * @param {Number} delay The milliseconds to delay
11997      * @param {Function} newFn (optional) Overrides function passed to constructor
11998      * @param {Object} newScope (optional) Overrides scope passed to constructor
11999      * @param {Array} newArgs (optional) Overrides args passed to constructor
12000      */
12001     this.delay = function(delay, newFn, newScope, newArgs){
12002         if(id && delay != d){
12003             this.cancel();
12004         }
12005         d = delay;
12006         t = new Date().getTime();
12007         fn = newFn || fn;
12008         scope = newScope || scope;
12009         args = newArgs || args;
12010         if(!id){
12011             id = setInterval(call, d);
12012         }
12013     };
12014
12015     /**
12016      * Cancel the last queued timeout
12017      */
12018     this.cancel = function(){
12019         if(id){
12020             clearInterval(id);
12021             id = null;
12022         }
12023     };
12024 };/*
12025  * Based on:
12026  * Ext JS Library 1.1.1
12027  * Copyright(c) 2006-2007, Ext JS, LLC.
12028  *
12029  * Originally Released Under LGPL - original licence link has changed is not relivant.
12030  *
12031  * Fork - LGPL
12032  * <script type="text/javascript">
12033  */
12034  
12035  
12036 Roo.util.TaskRunner = function(interval){
12037     interval = interval || 10;
12038     var tasks = [], removeQueue = [];
12039     var id = 0;
12040     var running = false;
12041
12042     var stopThread = function(){
12043         running = false;
12044         clearInterval(id);
12045         id = 0;
12046     };
12047
12048     var startThread = function(){
12049         if(!running){
12050             running = true;
12051             id = setInterval(runTasks, interval);
12052         }
12053     };
12054
12055     var removeTask = function(task){
12056         removeQueue.push(task);
12057         if(task.onStop){
12058             task.onStop();
12059         }
12060     };
12061
12062     var runTasks = function(){
12063         if(removeQueue.length > 0){
12064             for(var i = 0, len = removeQueue.length; i < len; i++){
12065                 tasks.remove(removeQueue[i]);
12066             }
12067             removeQueue = [];
12068             if(tasks.length < 1){
12069                 stopThread();
12070                 return;
12071             }
12072         }
12073         var now = new Date().getTime();
12074         for(var i = 0, len = tasks.length; i < len; ++i){
12075             var t = tasks[i];
12076             var itime = now - t.taskRunTime;
12077             if(t.interval <= itime){
12078                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12079                 t.taskRunTime = now;
12080                 if(rt === false || t.taskRunCount === t.repeat){
12081                     removeTask(t);
12082                     return;
12083                 }
12084             }
12085             if(t.duration && t.duration <= (now - t.taskStartTime)){
12086                 removeTask(t);
12087             }
12088         }
12089     };
12090
12091     /**
12092      * Queues a new task.
12093      * @param {Object} task
12094      */
12095     this.start = function(task){
12096         tasks.push(task);
12097         task.taskStartTime = new Date().getTime();
12098         task.taskRunTime = 0;
12099         task.taskRunCount = 0;
12100         startThread();
12101         return task;
12102     };
12103
12104     this.stop = function(task){
12105         removeTask(task);
12106         return task;
12107     };
12108
12109     this.stopAll = function(){
12110         stopThread();
12111         for(var i = 0, len = tasks.length; i < len; i++){
12112             if(tasks[i].onStop){
12113                 tasks[i].onStop();
12114             }
12115         }
12116         tasks = [];
12117         removeQueue = [];
12118     };
12119 };
12120
12121 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12122  * Based on:
12123  * Ext JS Library 1.1.1
12124  * Copyright(c) 2006-2007, Ext JS, LLC.
12125  *
12126  * Originally Released Under LGPL - original licence link has changed is not relivant.
12127  *
12128  * Fork - LGPL
12129  * <script type="text/javascript">
12130  */
12131
12132  
12133 /**
12134  * @class Roo.util.MixedCollection
12135  * @extends Roo.util.Observable
12136  * A Collection class that maintains both numeric indexes and keys and exposes events.
12137  * @constructor
12138  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12139  * collection (defaults to false)
12140  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12141  * and return the key value for that item.  This is used when available to look up the key on items that
12142  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12143  * equivalent to providing an implementation for the {@link #getKey} method.
12144  */
12145 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12146     this.items = [];
12147     this.map = {};
12148     this.keys = [];
12149     this.length = 0;
12150     this.addEvents({
12151         /**
12152          * @event clear
12153          * Fires when the collection is cleared.
12154          */
12155         "clear" : true,
12156         /**
12157          * @event add
12158          * Fires when an item is added to the collection.
12159          * @param {Number} index The index at which the item was added.
12160          * @param {Object} o The item added.
12161          * @param {String} key The key associated with the added item.
12162          */
12163         "add" : true,
12164         /**
12165          * @event replace
12166          * Fires when an item is replaced in the collection.
12167          * @param {String} key he key associated with the new added.
12168          * @param {Object} old The item being replaced.
12169          * @param {Object} new The new item.
12170          */
12171         "replace" : true,
12172         /**
12173          * @event remove
12174          * Fires when an item is removed from the collection.
12175          * @param {Object} o The item being removed.
12176          * @param {String} key (optional) The key associated with the removed item.
12177          */
12178         "remove" : true,
12179         "sort" : true
12180     });
12181     this.allowFunctions = allowFunctions === true;
12182     if(keyFn){
12183         this.getKey = keyFn;
12184     }
12185     Roo.util.MixedCollection.superclass.constructor.call(this);
12186 };
12187
12188 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12189     allowFunctions : false,
12190     
12191 /**
12192  * Adds an item to the collection.
12193  * @param {String} key The key to associate with the item
12194  * @param {Object} o The item to add.
12195  * @return {Object} The item added.
12196  */
12197     add : function(key, o){
12198         if(arguments.length == 1){
12199             o = arguments[0];
12200             key = this.getKey(o);
12201         }
12202         if(typeof key == "undefined" || key === null){
12203             this.length++;
12204             this.items.push(o);
12205             this.keys.push(null);
12206         }else{
12207             var old = this.map[key];
12208             if(old){
12209                 return this.replace(key, o);
12210             }
12211             this.length++;
12212             this.items.push(o);
12213             this.map[key] = o;
12214             this.keys.push(key);
12215         }
12216         this.fireEvent("add", this.length-1, o, key);
12217         return o;
12218     },
12219    
12220 /**
12221   * MixedCollection has a generic way to fetch keys if you implement getKey.
12222 <pre><code>
12223 // normal way
12224 var mc = new Roo.util.MixedCollection();
12225 mc.add(someEl.dom.id, someEl);
12226 mc.add(otherEl.dom.id, otherEl);
12227 //and so on
12228
12229 // using getKey
12230 var mc = new Roo.util.MixedCollection();
12231 mc.getKey = function(el){
12232    return el.dom.id;
12233 };
12234 mc.add(someEl);
12235 mc.add(otherEl);
12236
12237 // or via the constructor
12238 var mc = new Roo.util.MixedCollection(false, function(el){
12239    return el.dom.id;
12240 });
12241 mc.add(someEl);
12242 mc.add(otherEl);
12243 </code></pre>
12244  * @param o {Object} The item for which to find the key.
12245  * @return {Object} The key for the passed item.
12246  */
12247     getKey : function(o){
12248          return o.id; 
12249     },
12250    
12251 /**
12252  * Replaces an item in the collection.
12253  * @param {String} key The key associated with the item to replace, or the item to replace.
12254  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12255  * @return {Object}  The new item.
12256  */
12257     replace : function(key, o){
12258         if(arguments.length == 1){
12259             o = arguments[0];
12260             key = this.getKey(o);
12261         }
12262         var old = this.item(key);
12263         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12264              return this.add(key, o);
12265         }
12266         var index = this.indexOfKey(key);
12267         this.items[index] = o;
12268         this.map[key] = o;
12269         this.fireEvent("replace", key, old, o);
12270         return o;
12271     },
12272    
12273 /**
12274  * Adds all elements of an Array or an Object to the collection.
12275  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12276  * an Array of values, each of which are added to the collection.
12277  */
12278     addAll : function(objs){
12279         if(arguments.length > 1 || objs instanceof Array){
12280             var args = arguments.length > 1 ? arguments : objs;
12281             for(var i = 0, len = args.length; i < len; i++){
12282                 this.add(args[i]);
12283             }
12284         }else{
12285             for(var key in objs){
12286                 if(this.allowFunctions || typeof objs[key] != "function"){
12287                     this.add(key, objs[key]);
12288                 }
12289             }
12290         }
12291     },
12292    
12293 /**
12294  * Executes the specified function once for every item in the collection, passing each
12295  * item as the first and only parameter. returning false from the function will stop the iteration.
12296  * @param {Function} fn The function to execute for each item.
12297  * @param {Object} scope (optional) The scope in which to execute the function.
12298  */
12299     each : function(fn, scope){
12300         var items = [].concat(this.items); // each safe for removal
12301         for(var i = 0, len = items.length; i < len; i++){
12302             if(fn.call(scope || items[i], items[i], i, len) === false){
12303                 break;
12304             }
12305         }
12306     },
12307    
12308 /**
12309  * Executes the specified function once for every key in the collection, passing each
12310  * key, and its associated item as the first two parameters.
12311  * @param {Function} fn The function to execute for each item.
12312  * @param {Object} scope (optional) The scope in which to execute the function.
12313  */
12314     eachKey : function(fn, scope){
12315         for(var i = 0, len = this.keys.length; i < len; i++){
12316             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12317         }
12318     },
12319    
12320 /**
12321  * Returns the first item in the collection which elicits a true return value from the
12322  * passed selection function.
12323  * @param {Function} fn The selection function to execute for each item.
12324  * @param {Object} scope (optional) The scope in which to execute the function.
12325  * @return {Object} The first item in the collection which returned true from the selection function.
12326  */
12327     find : function(fn, scope){
12328         for(var i = 0, len = this.items.length; i < len; i++){
12329             if(fn.call(scope || window, this.items[i], this.keys[i])){
12330                 return this.items[i];
12331             }
12332         }
12333         return null;
12334     },
12335    
12336 /**
12337  * Inserts an item at the specified index in the collection.
12338  * @param {Number} index The index to insert the item at.
12339  * @param {String} key The key to associate with the new item, or the item itself.
12340  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12341  * @return {Object} The item inserted.
12342  */
12343     insert : function(index, key, o){
12344         if(arguments.length == 2){
12345             o = arguments[1];
12346             key = this.getKey(o);
12347         }
12348         if(index >= this.length){
12349             return this.add(key, o);
12350         }
12351         this.length++;
12352         this.items.splice(index, 0, o);
12353         if(typeof key != "undefined" && key != null){
12354             this.map[key] = o;
12355         }
12356         this.keys.splice(index, 0, key);
12357         this.fireEvent("add", index, o, key);
12358         return o;
12359     },
12360    
12361 /**
12362  * Removed an item from the collection.
12363  * @param {Object} o The item to remove.
12364  * @return {Object} The item removed.
12365  */
12366     remove : function(o){
12367         return this.removeAt(this.indexOf(o));
12368     },
12369    
12370 /**
12371  * Remove an item from a specified index in the collection.
12372  * @param {Number} index The index within the collection of the item to remove.
12373  */
12374     removeAt : function(index){
12375         if(index < this.length && index >= 0){
12376             this.length--;
12377             var o = this.items[index];
12378             this.items.splice(index, 1);
12379             var key = this.keys[index];
12380             if(typeof key != "undefined"){
12381                 delete this.map[key];
12382             }
12383             this.keys.splice(index, 1);
12384             this.fireEvent("remove", o, key);
12385         }
12386     },
12387    
12388 /**
12389  * Removed an item associated with the passed key fom the collection.
12390  * @param {String} key The key of the item to remove.
12391  */
12392     removeKey : function(key){
12393         return this.removeAt(this.indexOfKey(key));
12394     },
12395    
12396 /**
12397  * Returns the number of items in the collection.
12398  * @return {Number} the number of items in the collection.
12399  */
12400     getCount : function(){
12401         return this.length; 
12402     },
12403    
12404 /**
12405  * Returns index within the collection of the passed Object.
12406  * @param {Object} o The item to find the index of.
12407  * @return {Number} index of the item.
12408  */
12409     indexOf : function(o){
12410         if(!this.items.indexOf){
12411             for(var i = 0, len = this.items.length; i < len; i++){
12412                 if(this.items[i] == o) return i;
12413             }
12414             return -1;
12415         }else{
12416             return this.items.indexOf(o);
12417         }
12418     },
12419    
12420 /**
12421  * Returns index within the collection of the passed key.
12422  * @param {String} key The key to find the index of.
12423  * @return {Number} index of the key.
12424  */
12425     indexOfKey : function(key){
12426         if(!this.keys.indexOf){
12427             for(var i = 0, len = this.keys.length; i < len; i++){
12428                 if(this.keys[i] == key) return i;
12429             }
12430             return -1;
12431         }else{
12432             return this.keys.indexOf(key);
12433         }
12434     },
12435    
12436 /**
12437  * Returns the item associated with the passed key OR index. Key has priority over index.
12438  * @param {String/Number} key The key or index of the item.
12439  * @return {Object} The item associated with the passed key.
12440  */
12441     item : function(key){
12442         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12443         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12444     },
12445     
12446 /**
12447  * Returns the item at the specified index.
12448  * @param {Number} index The index of the item.
12449  * @return {Object}
12450  */
12451     itemAt : function(index){
12452         return this.items[index];
12453     },
12454     
12455 /**
12456  * Returns the item associated with the passed key.
12457  * @param {String/Number} key The key of the item.
12458  * @return {Object} The item associated with the passed key.
12459  */
12460     key : function(key){
12461         return this.map[key];
12462     },
12463    
12464 /**
12465  * Returns true if the collection contains the passed Object as an item.
12466  * @param {Object} o  The Object to look for in the collection.
12467  * @return {Boolean} True if the collection contains the Object as an item.
12468  */
12469     contains : function(o){
12470         return this.indexOf(o) != -1;
12471     },
12472    
12473 /**
12474  * Returns true if the collection contains the passed Object as a key.
12475  * @param {String} key The key to look for in the collection.
12476  * @return {Boolean} True if the collection contains the Object as a key.
12477  */
12478     containsKey : function(key){
12479         return typeof this.map[key] != "undefined";
12480     },
12481    
12482 /**
12483  * Removes all items from the collection.
12484  */
12485     clear : function(){
12486         this.length = 0;
12487         this.items = [];
12488         this.keys = [];
12489         this.map = {};
12490         this.fireEvent("clear");
12491     },
12492    
12493 /**
12494  * Returns the first item in the collection.
12495  * @return {Object} the first item in the collection..
12496  */
12497     first : function(){
12498         return this.items[0]; 
12499     },
12500    
12501 /**
12502  * Returns the last item in the collection.
12503  * @return {Object} the last item in the collection..
12504  */
12505     last : function(){
12506         return this.items[this.length-1];   
12507     },
12508     
12509     _sort : function(property, dir, fn){
12510         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12511         fn = fn || function(a, b){
12512             return a-b;
12513         };
12514         var c = [], k = this.keys, items = this.items;
12515         for(var i = 0, len = items.length; i < len; i++){
12516             c[c.length] = {key: k[i], value: items[i], index: i};
12517         }
12518         c.sort(function(a, b){
12519             var v = fn(a[property], b[property]) * dsc;
12520             if(v == 0){
12521                 v = (a.index < b.index ? -1 : 1);
12522             }
12523             return v;
12524         });
12525         for(var i = 0, len = c.length; i < len; i++){
12526             items[i] = c[i].value;
12527             k[i] = c[i].key;
12528         }
12529         this.fireEvent("sort", this);
12530     },
12531     
12532     /**
12533      * Sorts this collection with the passed comparison function
12534      * @param {String} direction (optional) "ASC" or "DESC"
12535      * @param {Function} fn (optional) comparison function
12536      */
12537     sort : function(dir, fn){
12538         this._sort("value", dir, fn);
12539     },
12540     
12541     /**
12542      * Sorts this collection by keys
12543      * @param {String} direction (optional) "ASC" or "DESC"
12544      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12545      */
12546     keySort : function(dir, fn){
12547         this._sort("key", dir, fn || function(a, b){
12548             return String(a).toUpperCase()-String(b).toUpperCase();
12549         });
12550     },
12551     
12552     /**
12553      * Returns a range of items in this collection
12554      * @param {Number} startIndex (optional) defaults to 0
12555      * @param {Number} endIndex (optional) default to the last item
12556      * @return {Array} An array of items
12557      */
12558     getRange : function(start, end){
12559         var items = this.items;
12560         if(items.length < 1){
12561             return [];
12562         }
12563         start = start || 0;
12564         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12565         var r = [];
12566         if(start <= end){
12567             for(var i = start; i <= end; i++) {
12568                     r[r.length] = items[i];
12569             }
12570         }else{
12571             for(var i = start; i >= end; i--) {
12572                     r[r.length] = items[i];
12573             }
12574         }
12575         return r;
12576     },
12577         
12578     /**
12579      * Filter the <i>objects</i> in this collection by a specific property. 
12580      * Returns a new collection that has been filtered.
12581      * @param {String} property A property on your objects
12582      * @param {String/RegExp} value Either string that the property values 
12583      * should start with or a RegExp to test against the property
12584      * @return {MixedCollection} The new filtered collection
12585      */
12586     filter : function(property, value){
12587         if(!value.exec){ // not a regex
12588             value = String(value);
12589             if(value.length == 0){
12590                 return this.clone();
12591             }
12592             value = new RegExp("^" + Roo.escapeRe(value), "i");
12593         }
12594         return this.filterBy(function(o){
12595             return o && value.test(o[property]);
12596         });
12597         },
12598     
12599     /**
12600      * Filter by a function. * Returns a new collection that has been filtered.
12601      * The passed function will be called with each 
12602      * object in the collection. If the function returns true, the value is included 
12603      * otherwise it is filtered.
12604      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12605      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12606      * @return {MixedCollection} The new filtered collection
12607      */
12608     filterBy : function(fn, scope){
12609         var r = new Roo.util.MixedCollection();
12610         r.getKey = this.getKey;
12611         var k = this.keys, it = this.items;
12612         for(var i = 0, len = it.length; i < len; i++){
12613             if(fn.call(scope||this, it[i], k[i])){
12614                                 r.add(k[i], it[i]);
12615                         }
12616         }
12617         return r;
12618     },
12619     
12620     /**
12621      * Creates a duplicate of this collection
12622      * @return {MixedCollection}
12623      */
12624     clone : function(){
12625         var r = new Roo.util.MixedCollection();
12626         var k = this.keys, it = this.items;
12627         for(var i = 0, len = it.length; i < len; i++){
12628             r.add(k[i], it[i]);
12629         }
12630         r.getKey = this.getKey;
12631         return r;
12632     }
12633 });
12634 /**
12635  * Returns the item associated with the passed key or index.
12636  * @method
12637  * @param {String/Number} key The key or index of the item.
12638  * @return {Object} The item associated with the passed key.
12639  */
12640 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12641  * Based on:
12642  * Ext JS Library 1.1.1
12643  * Copyright(c) 2006-2007, Ext JS, LLC.
12644  *
12645  * Originally Released Under LGPL - original licence link has changed is not relivant.
12646  *
12647  * Fork - LGPL
12648  * <script type="text/javascript">
12649  */
12650 /**
12651  * @class Roo.util.JSON
12652  * Modified version of Douglas Crockford"s json.js that doesn"t
12653  * mess with the Object prototype 
12654  * http://www.json.org/js.html
12655  * @singleton
12656  */
12657 Roo.util.JSON = new (function(){
12658     var useHasOwn = {}.hasOwnProperty ? true : false;
12659     
12660     // crashes Safari in some instances
12661     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12662     
12663     var pad = function(n) {
12664         return n < 10 ? "0" + n : n;
12665     };
12666     
12667     var m = {
12668         "\b": '\\b',
12669         "\t": '\\t',
12670         "\n": '\\n',
12671         "\f": '\\f',
12672         "\r": '\\r',
12673         '"' : '\\"',
12674         "\\": '\\\\'
12675     };
12676
12677     var encodeString = function(s){
12678         if (/["\\\x00-\x1f]/.test(s)) {
12679             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12680                 var c = m[b];
12681                 if(c){
12682                     return c;
12683                 }
12684                 c = b.charCodeAt();
12685                 return "\\u00" +
12686                     Math.floor(c / 16).toString(16) +
12687                     (c % 16).toString(16);
12688             }) + '"';
12689         }
12690         return '"' + s + '"';
12691     };
12692     
12693     var encodeArray = function(o){
12694         var a = ["["], b, i, l = o.length, v;
12695             for (i = 0; i < l; i += 1) {
12696                 v = o[i];
12697                 switch (typeof v) {
12698                     case "undefined":
12699                     case "function":
12700                     case "unknown":
12701                         break;
12702                     default:
12703                         if (b) {
12704                             a.push(',');
12705                         }
12706                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12707                         b = true;
12708                 }
12709             }
12710             a.push("]");
12711             return a.join("");
12712     };
12713     
12714     var encodeDate = function(o){
12715         return '"' + o.getFullYear() + "-" +
12716                 pad(o.getMonth() + 1) + "-" +
12717                 pad(o.getDate()) + "T" +
12718                 pad(o.getHours()) + ":" +
12719                 pad(o.getMinutes()) + ":" +
12720                 pad(o.getSeconds()) + '"';
12721     };
12722     
12723     /**
12724      * Encodes an Object, Array or other value
12725      * @param {Mixed} o The variable to encode
12726      * @return {String} The JSON string
12727      */
12728     this.encode = function(o){
12729         if(typeof o == "undefined" || o === null){
12730             return "null";
12731         }else if(o instanceof Array){
12732             return encodeArray(o);
12733         }else if(o instanceof Date){
12734             return encodeDate(o);
12735         }else if(typeof o == "string"){
12736             return encodeString(o);
12737         }else if(typeof o == "number"){
12738             return isFinite(o) ? String(o) : "null";
12739         }else if(typeof o == "boolean"){
12740             return String(o);
12741         }else {
12742             var a = ["{"], b, i, v;
12743             for (i in o) {
12744                 if(!useHasOwn || o.hasOwnProperty(i)) {
12745                     v = o[i];
12746                     switch (typeof v) {
12747                     case "undefined":
12748                     case "function":
12749                     case "unknown":
12750                         break;
12751                     default:
12752                         if(b){
12753                             a.push(',');
12754                         }
12755                         a.push(this.encode(i), ":",
12756                                 v === null ? "null" : this.encode(v));
12757                         b = true;
12758                     }
12759                 }
12760             }
12761             a.push("}");
12762             return a.join("");
12763         }
12764     };
12765     
12766     /**
12767      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12768      * @param {String} json The JSON string
12769      * @return {Object} The resulting object
12770      */
12771     this.decode = function(json){
12772         /**
12773          * eval:var:json
12774          */
12775         return eval("(" + json + ')');
12776     };
12777 })();
12778 /** 
12779  * Shorthand for {@link Roo.util.JSON#encode}
12780  * @member Roo encode 
12781  * @method */
12782 Roo.encode = Roo.util.JSON.encode;
12783 /** 
12784  * Shorthand for {@link Roo.util.JSON#decode}
12785  * @member Roo decode 
12786  * @method */
12787 Roo.decode = Roo.util.JSON.decode;
12788 /*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798  
12799 /**
12800  * @class Roo.util.Format
12801  * Reusable data formatting functions
12802  * @singleton
12803  */
12804 Roo.util.Format = function(){
12805     var trimRe = /^\s+|\s+$/g;
12806     return {
12807         /**
12808          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12809          * @param {String} value The string to truncate
12810          * @param {Number} length The maximum length to allow before truncating
12811          * @return {String} The converted text
12812          */
12813         ellipsis : function(value, len){
12814             if(value && value.length > len){
12815                 return value.substr(0, len-3)+"...";
12816             }
12817             return value;
12818         },
12819
12820         /**
12821          * Checks a reference and converts it to empty string if it is undefined
12822          * @param {Mixed} value Reference to check
12823          * @return {Mixed} Empty string if converted, otherwise the original value
12824          */
12825         undef : function(value){
12826             return typeof value != "undefined" ? value : "";
12827         },
12828
12829         /**
12830          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12831          * @param {String} value The string to encode
12832          * @return {String} The encoded text
12833          */
12834         htmlEncode : function(value){
12835             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12836         },
12837
12838         /**
12839          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12840          * @param {String} value The string to decode
12841          * @return {String} The decoded text
12842          */
12843         htmlDecode : function(value){
12844             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12845         },
12846
12847         /**
12848          * Trims any whitespace from either side of a string
12849          * @param {String} value The text to trim
12850          * @return {String} The trimmed text
12851          */
12852         trim : function(value){
12853             return String(value).replace(trimRe, "");
12854         },
12855
12856         /**
12857          * Returns a substring from within an original string
12858          * @param {String} value The original text
12859          * @param {Number} start The start index of the substring
12860          * @param {Number} length The length of the substring
12861          * @return {String} The substring
12862          */
12863         substr : function(value, start, length){
12864             return String(value).substr(start, length);
12865         },
12866
12867         /**
12868          * Converts a string to all lower case letters
12869          * @param {String} value The text to convert
12870          * @return {String} The converted text
12871          */
12872         lowercase : function(value){
12873             return String(value).toLowerCase();
12874         },
12875
12876         /**
12877          * Converts a string to all upper case letters
12878          * @param {String} value The text to convert
12879          * @return {String} The converted text
12880          */
12881         uppercase : function(value){
12882             return String(value).toUpperCase();
12883         },
12884
12885         /**
12886          * Converts the first character only of a string to upper case
12887          * @param {String} value The text to convert
12888          * @return {String} The converted text
12889          */
12890         capitalize : function(value){
12891             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12892         },
12893
12894         // private
12895         call : function(value, fn){
12896             if(arguments.length > 2){
12897                 var args = Array.prototype.slice.call(arguments, 2);
12898                 args.unshift(value);
12899                  
12900                 return /** eval:var:value */  eval(fn).apply(window, args);
12901             }else{
12902                 /** eval:var:value */
12903                 return /** eval:var:value */ eval(fn).call(window, value);
12904             }
12905         },
12906
12907         /**
12908          * Format a number as US currency
12909          * @param {Number/String} value The numeric value to format
12910          * @return {String} The formatted currency string
12911          */
12912         usMoney : function(v){
12913             v = (Math.round((v-0)*100))/100;
12914             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12915             v = String(v);
12916             var ps = v.split('.');
12917             var whole = ps[0];
12918             var sub = ps[1] ? '.'+ ps[1] : '.00';
12919             var r = /(\d+)(\d{3})/;
12920             while (r.test(whole)) {
12921                 whole = whole.replace(r, '$1' + ',' + '$2');
12922             }
12923             return "$" + whole + sub ;
12924         },
12925
12926         /**
12927          * Parse a value into a formatted date using the specified format pattern.
12928          * @param {Mixed} value The value to format
12929          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12930          * @return {String} The formatted date string
12931          */
12932         date : function(v, format){
12933             if(!v){
12934                 return "";
12935             }
12936             if(!(v instanceof Date)){
12937                 v = new Date(Date.parse(v));
12938             }
12939             return v.dateFormat(format || "m/d/Y");
12940         },
12941
12942         /**
12943          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12944          * @param {String} format Any valid date format string
12945          * @return {Function} The date formatting function
12946          */
12947         dateRenderer : function(format){
12948             return function(v){
12949                 return Roo.util.Format.date(v, format);  
12950             };
12951         },
12952
12953         // private
12954         stripTagsRE : /<\/?[^>]+>/gi,
12955         
12956         /**
12957          * Strips all HTML tags
12958          * @param {Mixed} value The text from which to strip tags
12959          * @return {String} The stripped text
12960          */
12961         stripTags : function(v){
12962             return !v ? v : String(v).replace(this.stripTagsRE, "");
12963         }
12964     };
12965 }();/*
12966  * Based on:
12967  * Ext JS Library 1.1.1
12968  * Copyright(c) 2006-2007, Ext JS, LLC.
12969  *
12970  * Originally Released Under LGPL - original licence link has changed is not relivant.
12971  *
12972  * Fork - LGPL
12973  * <script type="text/javascript">
12974  */
12975
12976
12977  
12978
12979 /**
12980  * @class Roo.MasterTemplate
12981  * @extends Roo.Template
12982  * Provides a template that can have child templates. The syntax is:
12983 <pre><code>
12984 var t = new Roo.MasterTemplate(
12985         '&lt;select name="{name}"&gt;',
12986                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12987         '&lt;/select&gt;'
12988 );
12989 t.add('options', {value: 'foo', text: 'bar'});
12990 // or you can add multiple child elements in one shot
12991 t.addAll('options', [
12992     {value: 'foo', text: 'bar'},
12993     {value: 'foo2', text: 'bar2'},
12994     {value: 'foo3', text: 'bar3'}
12995 ]);
12996 // then append, applying the master template values
12997 t.append('my-form', {name: 'my-select'});
12998 </code></pre>
12999 * A name attribute for the child template is not required if you have only one child
13000 * template or you want to refer to them by index.
13001  */
13002 Roo.MasterTemplate = function(){
13003     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13004     this.originalHtml = this.html;
13005     var st = {};
13006     var m, re = this.subTemplateRe;
13007     re.lastIndex = 0;
13008     var subIndex = 0;
13009     while(m = re.exec(this.html)){
13010         var name = m[1], content = m[2];
13011         st[subIndex] = {
13012             name: name,
13013             index: subIndex,
13014             buffer: [],
13015             tpl : new Roo.Template(content)
13016         };
13017         if(name){
13018             st[name] = st[subIndex];
13019         }
13020         st[subIndex].tpl.compile();
13021         st[subIndex].tpl.call = this.call.createDelegate(this);
13022         subIndex++;
13023     }
13024     this.subCount = subIndex;
13025     this.subs = st;
13026 };
13027 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13028     /**
13029     * The regular expression used to match sub templates
13030     * @type RegExp
13031     * @property
13032     */
13033     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13034
13035     /**
13036      * Applies the passed values to a child template.
13037      * @param {String/Number} name (optional) The name or index of the child template
13038      * @param {Array/Object} values The values to be applied to the template
13039      * @return {MasterTemplate} this
13040      */
13041      add : function(name, values){
13042         if(arguments.length == 1){
13043             values = arguments[0];
13044             name = 0;
13045         }
13046         var s = this.subs[name];
13047         s.buffer[s.buffer.length] = s.tpl.apply(values);
13048         return this;
13049     },
13050
13051     /**
13052      * Applies all the passed values to a child template.
13053      * @param {String/Number} name (optional) The name or index of the child template
13054      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13055      * @param {Boolean} reset (optional) True to reset the template first
13056      * @return {MasterTemplate} this
13057      */
13058     fill : function(name, values, reset){
13059         var a = arguments;
13060         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13061             values = a[0];
13062             name = 0;
13063             reset = a[1];
13064         }
13065         if(reset){
13066             this.reset();
13067         }
13068         for(var i = 0, len = values.length; i < len; i++){
13069             this.add(name, values[i]);
13070         }
13071         return this;
13072     },
13073
13074     /**
13075      * Resets the template for reuse
13076      * @return {MasterTemplate} this
13077      */
13078      reset : function(){
13079         var s = this.subs;
13080         for(var i = 0; i < this.subCount; i++){
13081             s[i].buffer = [];
13082         }
13083         return this;
13084     },
13085
13086     applyTemplate : function(values){
13087         var s = this.subs;
13088         var replaceIndex = -1;
13089         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13090             return s[++replaceIndex].buffer.join("");
13091         });
13092         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13093     },
13094
13095     apply : function(){
13096         return this.applyTemplate.apply(this, arguments);
13097     },
13098
13099     compile : function(){return this;}
13100 });
13101
13102 /**
13103  * Alias for fill().
13104  * @method
13105  */
13106 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13107  /**
13108  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13109  * var tpl = Roo.MasterTemplate.from('element-id');
13110  * @param {String/HTMLElement} el
13111  * @param {Object} config
13112  * @static
13113  */
13114 Roo.MasterTemplate.from = function(el, config){
13115     el = Roo.getDom(el);
13116     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13117 };/*
13118  * Based on:
13119  * Ext JS Library 1.1.1
13120  * Copyright(c) 2006-2007, Ext JS, LLC.
13121  *
13122  * Originally Released Under LGPL - original licence link has changed is not relivant.
13123  *
13124  * Fork - LGPL
13125  * <script type="text/javascript">
13126  */
13127
13128  
13129 /**
13130  * @class Roo.util.CSS
13131  * Utility class for manipulating CSS rules
13132  * @singleton
13133  */
13134 Roo.util.CSS = function(){
13135         var rules = null;
13136         var doc = document;
13137
13138     var camelRe = /(-[a-z])/gi;
13139     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13140
13141    return {
13142    /**
13143     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13144     * tag and appended to the HEAD of the document.
13145     * @param {String} cssText The text containing the css rules
13146     * @param {String} id An id to add to the stylesheet for later removal
13147     * @return {StyleSheet}
13148     */
13149    createStyleSheet : function(cssText, id){
13150        var ss;
13151        var head = doc.getElementsByTagName("head")[0];
13152        var rules = doc.createElement("style");
13153        rules.setAttribute("type", "text/css");
13154        if(id){
13155            rules.setAttribute("id", id);
13156        }
13157        if(Roo.isIE){
13158            head.appendChild(rules);
13159            ss = rules.styleSheet;
13160            ss.cssText = cssText;
13161        }else{
13162            try{
13163                 rules.appendChild(doc.createTextNode(cssText));
13164            }catch(e){
13165                rules.cssText = cssText; 
13166            }
13167            head.appendChild(rules);
13168            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13169        }
13170        this.cacheStyleSheet(ss);
13171        return ss;
13172    },
13173
13174    /**
13175     * Removes a style or link tag by id
13176     * @param {String} id The id of the tag
13177     */
13178    removeStyleSheet : function(id){
13179        var existing = doc.getElementById(id);
13180        if(existing){
13181            existing.parentNode.removeChild(existing);
13182        }
13183    },
13184
13185    /**
13186     * Dynamically swaps an existing stylesheet reference for a new one
13187     * @param {String} id The id of an existing link tag to remove
13188     * @param {String} url The href of the new stylesheet to include
13189     */
13190    swapStyleSheet : function(id, url){
13191        this.removeStyleSheet(id);
13192        var ss = doc.createElement("link");
13193        ss.setAttribute("rel", "stylesheet");
13194        ss.setAttribute("type", "text/css");
13195        ss.setAttribute("id", id);
13196        ss.setAttribute("href", url);
13197        doc.getElementsByTagName("head")[0].appendChild(ss);
13198    },
13199    
13200    /**
13201     * Refresh the rule cache if you have dynamically added stylesheets
13202     * @return {Object} An object (hash) of rules indexed by selector
13203     */
13204    refreshCache : function(){
13205        return this.getRules(true);
13206    },
13207
13208    // private
13209    cacheStyleSheet : function(ss){
13210        if(!rules){
13211            rules = {};
13212        }
13213        try{// try catch for cross domain access issue
13214            var ssRules = ss.cssRules || ss.rules;
13215            for(var j = ssRules.length-1; j >= 0; --j){
13216                rules[ssRules[j].selectorText] = ssRules[j];
13217            }
13218        }catch(e){}
13219    },
13220    
13221    /**
13222     * Gets all css rules for the document
13223     * @param {Boolean} refreshCache true to refresh the internal cache
13224     * @return {Object} An object (hash) of rules indexed by selector
13225     */
13226    getRules : function(refreshCache){
13227                 if(rules == null || refreshCache){
13228                         rules = {};
13229                         var ds = doc.styleSheets;
13230                         for(var i =0, len = ds.length; i < len; i++){
13231                             try{
13232                         this.cacheStyleSheet(ds[i]);
13233                     }catch(e){} 
13234                 }
13235                 }
13236                 return rules;
13237         },
13238         
13239         /**
13240     * Gets an an individual CSS rule by selector(s)
13241     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13242     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13243     * @return {CSSRule} The CSS rule or null if one is not found
13244     */
13245    getRule : function(selector, refreshCache){
13246                 var rs = this.getRules(refreshCache);
13247                 if(!(selector instanceof Array)){
13248                     return rs[selector];
13249                 }
13250                 for(var i = 0; i < selector.length; i++){
13251                         if(rs[selector[i]]){
13252                                 return rs[selector[i]];
13253                         }
13254                 }
13255                 return null;
13256         },
13257         
13258         
13259         /**
13260     * Updates a rule property
13261     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13262     * @param {String} property The css property
13263     * @param {String} value The new value for the property
13264     * @return {Boolean} true If a rule was found and updated
13265     */
13266    updateRule : function(selector, property, value){
13267                 if(!(selector instanceof Array)){
13268                         var rule = this.getRule(selector);
13269                         if(rule){
13270                                 rule.style[property.replace(camelRe, camelFn)] = value;
13271                                 return true;
13272                         }
13273                 }else{
13274                         for(var i = 0; i < selector.length; i++){
13275                                 if(this.updateRule(selector[i], property, value)){
13276                                         return true;
13277                                 }
13278                         }
13279                 }
13280                 return false;
13281         }
13282    };   
13283 }();/*
13284  * Based on:
13285  * Ext JS Library 1.1.1
13286  * Copyright(c) 2006-2007, Ext JS, LLC.
13287  *
13288  * Originally Released Under LGPL - original licence link has changed is not relivant.
13289  *
13290  * Fork - LGPL
13291  * <script type="text/javascript">
13292  */
13293
13294  
13295
13296 /**
13297  * @class Roo.util.ClickRepeater
13298  * @extends Roo.util.Observable
13299  * 
13300  * A wrapper class which can be applied to any element. Fires a "click" event while the
13301  * mouse is pressed. The interval between firings may be specified in the config but
13302  * defaults to 10 milliseconds.
13303  * 
13304  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13305  * 
13306  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13307  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13308  * Similar to an autorepeat key delay.
13309  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13310  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13311  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13312  *           "interval" and "delay" are ignored. "immediate" is honored.
13313  * @cfg {Boolean} preventDefault True to prevent the default click event
13314  * @cfg {Boolean} stopDefault True to stop the default click event
13315  * 
13316  * @history
13317  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13318  *     2007-02-02 jvs Renamed to ClickRepeater
13319  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13320  *
13321  *  @constructor
13322  * @param {String/HTMLElement/Element} el The element to listen on
13323  * @param {Object} config
13324  **/
13325 Roo.util.ClickRepeater = function(el, config)
13326 {
13327     this.el = Roo.get(el);
13328     this.el.unselectable();
13329
13330     Roo.apply(this, config);
13331
13332     this.addEvents({
13333     /**
13334      * @event mousedown
13335      * Fires when the mouse button is depressed.
13336      * @param {Roo.util.ClickRepeater} this
13337      */
13338         "mousedown" : true,
13339     /**
13340      * @event click
13341      * Fires on a specified interval during the time the element is pressed.
13342      * @param {Roo.util.ClickRepeater} this
13343      */
13344         "click" : true,
13345     /**
13346      * @event mouseup
13347      * Fires when the mouse key is released.
13348      * @param {Roo.util.ClickRepeater} this
13349      */
13350         "mouseup" : true
13351     });
13352
13353     this.el.on("mousedown", this.handleMouseDown, this);
13354     if(this.preventDefault || this.stopDefault){
13355         this.el.on("click", function(e){
13356             if(this.preventDefault){
13357                 e.preventDefault();
13358             }
13359             if(this.stopDefault){
13360                 e.stopEvent();
13361             }
13362         }, this);
13363     }
13364
13365     // allow inline handler
13366     if(this.handler){
13367         this.on("click", this.handler,  this.scope || this);
13368     }
13369
13370     Roo.util.ClickRepeater.superclass.constructor.call(this);
13371 };
13372
13373 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13374     interval : 20,
13375     delay: 250,
13376     preventDefault : true,
13377     stopDefault : false,
13378     timer : 0,
13379
13380     // private
13381     handleMouseDown : function(){
13382         clearTimeout(this.timer);
13383         this.el.blur();
13384         if(this.pressClass){
13385             this.el.addClass(this.pressClass);
13386         }
13387         this.mousedownTime = new Date();
13388
13389         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13390         this.el.on("mouseout", this.handleMouseOut, this);
13391
13392         this.fireEvent("mousedown", this);
13393         this.fireEvent("click", this);
13394         
13395         this.timer = this.click.defer(this.delay || this.interval, this);
13396     },
13397
13398     // private
13399     click : function(){
13400         this.fireEvent("click", this);
13401         this.timer = this.click.defer(this.getInterval(), this);
13402     },
13403
13404     // private
13405     getInterval: function(){
13406         if(!this.accelerate){
13407             return this.interval;
13408         }
13409         var pressTime = this.mousedownTime.getElapsed();
13410         if(pressTime < 500){
13411             return 400;
13412         }else if(pressTime < 1700){
13413             return 320;
13414         }else if(pressTime < 2600){
13415             return 250;
13416         }else if(pressTime < 3500){
13417             return 180;
13418         }else if(pressTime < 4400){
13419             return 140;
13420         }else if(pressTime < 5300){
13421             return 80;
13422         }else if(pressTime < 6200){
13423             return 50;
13424         }else{
13425             return 10;
13426         }
13427     },
13428
13429     // private
13430     handleMouseOut : function(){
13431         clearTimeout(this.timer);
13432         if(this.pressClass){
13433             this.el.removeClass(this.pressClass);
13434         }
13435         this.el.on("mouseover", this.handleMouseReturn, this);
13436     },
13437
13438     // private
13439     handleMouseReturn : function(){
13440         this.el.un("mouseover", this.handleMouseReturn);
13441         if(this.pressClass){
13442             this.el.addClass(this.pressClass);
13443         }
13444         this.click();
13445     },
13446
13447     // private
13448     handleMouseUp : function(){
13449         clearTimeout(this.timer);
13450         this.el.un("mouseover", this.handleMouseReturn);
13451         this.el.un("mouseout", this.handleMouseOut);
13452         Roo.get(document).un("mouseup", this.handleMouseUp);
13453         this.el.removeClass(this.pressClass);
13454         this.fireEvent("mouseup", this);
13455     }
13456 });/*
13457  * Based on:
13458  * Ext JS Library 1.1.1
13459  * Copyright(c) 2006-2007, Ext JS, LLC.
13460  *
13461  * Originally Released Under LGPL - original licence link has changed is not relivant.
13462  *
13463  * Fork - LGPL
13464  * <script type="text/javascript">
13465  */
13466
13467  
13468 /**
13469  * @class Roo.KeyNav
13470  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13471  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13472  * way to implement custom navigation schemes for any UI component.</p>
13473  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13474  * pageUp, pageDown, del, home, end.  Usage:</p>
13475  <pre><code>
13476 var nav = new Roo.KeyNav("my-element", {
13477     "left" : function(e){
13478         this.moveLeft(e.ctrlKey);
13479     },
13480     "right" : function(e){
13481         this.moveRight(e.ctrlKey);
13482     },
13483     "enter" : function(e){
13484         this.save();
13485     },
13486     scope : this
13487 });
13488 </code></pre>
13489  * @constructor
13490  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13491  * @param {Object} config The config
13492  */
13493 Roo.KeyNav = function(el, config){
13494     this.el = Roo.get(el);
13495     Roo.apply(this, config);
13496     if(!this.disabled){
13497         this.disabled = true;
13498         this.enable();
13499     }
13500 };
13501
13502 Roo.KeyNav.prototype = {
13503     /**
13504      * @cfg {Boolean} disabled
13505      * True to disable this KeyNav instance (defaults to false)
13506      */
13507     disabled : false,
13508     /**
13509      * @cfg {String} defaultEventAction
13510      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13511      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13512      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13513      */
13514     defaultEventAction: "stopEvent",
13515     /**
13516      * @cfg {Boolean} forceKeyDown
13517      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13518      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13519      * handle keydown instead of keypress.
13520      */
13521     forceKeyDown : false,
13522
13523     // private
13524     prepareEvent : function(e){
13525         var k = e.getKey();
13526         var h = this.keyToHandler[k];
13527         //if(h && this[h]){
13528         //    e.stopPropagation();
13529         //}
13530         if(Roo.isSafari && h && k >= 37 && k <= 40){
13531             e.stopEvent();
13532         }
13533     },
13534
13535     // private
13536     relay : function(e){
13537         var k = e.getKey();
13538         var h = this.keyToHandler[k];
13539         if(h && this[h]){
13540             if(this.doRelay(e, this[h], h) !== true){
13541                 e[this.defaultEventAction]();
13542             }
13543         }
13544     },
13545
13546     // private
13547     doRelay : function(e, h, hname){
13548         return h.call(this.scope || this, e);
13549     },
13550
13551     // possible handlers
13552     enter : false,
13553     left : false,
13554     right : false,
13555     up : false,
13556     down : false,
13557     tab : false,
13558     esc : false,
13559     pageUp : false,
13560     pageDown : false,
13561     del : false,
13562     home : false,
13563     end : false,
13564
13565     // quick lookup hash
13566     keyToHandler : {
13567         37 : "left",
13568         39 : "right",
13569         38 : "up",
13570         40 : "down",
13571         33 : "pageUp",
13572         34 : "pageDown",
13573         46 : "del",
13574         36 : "home",
13575         35 : "end",
13576         13 : "enter",
13577         27 : "esc",
13578         9  : "tab"
13579     },
13580
13581         /**
13582          * Enable this KeyNav
13583          */
13584         enable: function(){
13585                 if(this.disabled){
13586             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13587             // the EventObject will normalize Safari automatically
13588             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13589                 this.el.on("keydown", this.relay,  this);
13590             }else{
13591                 this.el.on("keydown", this.prepareEvent,  this);
13592                 this.el.on("keypress", this.relay,  this);
13593             }
13594                     this.disabled = false;
13595                 }
13596         },
13597
13598         /**
13599          * Disable this KeyNav
13600          */
13601         disable: function(){
13602                 if(!this.disabled){
13603                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13604                 this.el.un("keydown", this.relay);
13605             }else{
13606                 this.el.un("keydown", this.prepareEvent);
13607                 this.el.un("keypress", this.relay);
13608             }
13609                     this.disabled = true;
13610                 }
13611         }
13612 };/*
13613  * Based on:
13614  * Ext JS Library 1.1.1
13615  * Copyright(c) 2006-2007, Ext JS, LLC.
13616  *
13617  * Originally Released Under LGPL - original licence link has changed is not relivant.
13618  *
13619  * Fork - LGPL
13620  * <script type="text/javascript">
13621  */
13622
13623  
13624 /**
13625  * @class Roo.KeyMap
13626  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13627  * The constructor accepts the same config object as defined by {@link #addBinding}.
13628  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13629  * combination it will call the function with this signature (if the match is a multi-key
13630  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13631  * A KeyMap can also handle a string representation of keys.<br />
13632  * Usage:
13633  <pre><code>
13634 // map one key by key code
13635 var map = new Roo.KeyMap("my-element", {
13636     key: 13, // or Roo.EventObject.ENTER
13637     fn: myHandler,
13638     scope: myObject
13639 });
13640
13641 // map multiple keys to one action by string
13642 var map = new Roo.KeyMap("my-element", {
13643     key: "a\r\n\t",
13644     fn: myHandler,
13645     scope: myObject
13646 });
13647
13648 // map multiple keys to multiple actions by strings and array of codes
13649 var map = new Roo.KeyMap("my-element", [
13650     {
13651         key: [10,13],
13652         fn: function(){ alert("Return was pressed"); }
13653     }, {
13654         key: "abc",
13655         fn: function(){ alert('a, b or c was pressed'); }
13656     }, {
13657         key: "\t",
13658         ctrl:true,
13659         shift:true,
13660         fn: function(){ alert('Control + shift + tab was pressed.'); }
13661     }
13662 ]);
13663 </code></pre>
13664  * <b>Note: A KeyMap starts enabled</b>
13665  * @constructor
13666  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13667  * @param {Object} config The config (see {@link #addBinding})
13668  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13669  */
13670 Roo.KeyMap = function(el, config, eventName){
13671     this.el  = Roo.get(el);
13672     this.eventName = eventName || "keydown";
13673     this.bindings = [];
13674     if(config){
13675         this.addBinding(config);
13676     }
13677     this.enable();
13678 };
13679
13680 Roo.KeyMap.prototype = {
13681     /**
13682      * True to stop the event from bubbling and prevent the default browser action if the
13683      * key was handled by the KeyMap (defaults to false)
13684      * @type Boolean
13685      */
13686     stopEvent : false,
13687
13688     /**
13689      * Add a new binding to this KeyMap. The following config object properties are supported:
13690      * <pre>
13691 Property    Type             Description
13692 ----------  ---------------  ----------------------------------------------------------------------
13693 key         String/Array     A single keycode or an array of keycodes to handle
13694 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13695 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13696 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13697 fn          Function         The function to call when KeyMap finds the expected key combination
13698 scope       Object           The scope of the callback function
13699 </pre>
13700      *
13701      * Usage:
13702      * <pre><code>
13703 // Create a KeyMap
13704 var map = new Roo.KeyMap(document, {
13705     key: Roo.EventObject.ENTER,
13706     fn: handleKey,
13707     scope: this
13708 });
13709
13710 //Add a new binding to the existing KeyMap later
13711 map.addBinding({
13712     key: 'abc',
13713     shift: true,
13714     fn: handleKey,
13715     scope: this
13716 });
13717 </code></pre>
13718      * @param {Object/Array} config A single KeyMap config or an array of configs
13719      */
13720         addBinding : function(config){
13721         if(config instanceof Array){
13722             for(var i = 0, len = config.length; i < len; i++){
13723                 this.addBinding(config[i]);
13724             }
13725             return;
13726         }
13727         var keyCode = config.key,
13728             shift = config.shift, 
13729             ctrl = config.ctrl, 
13730             alt = config.alt,
13731             fn = config.fn,
13732             scope = config.scope;
13733         if(typeof keyCode == "string"){
13734             var ks = [];
13735             var keyString = keyCode.toUpperCase();
13736             for(var j = 0, len = keyString.length; j < len; j++){
13737                 ks.push(keyString.charCodeAt(j));
13738             }
13739             keyCode = ks;
13740         }
13741         var keyArray = keyCode instanceof Array;
13742         var handler = function(e){
13743             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13744                 var k = e.getKey();
13745                 if(keyArray){
13746                     for(var i = 0, len = keyCode.length; i < len; i++){
13747                         if(keyCode[i] == k){
13748                           if(this.stopEvent){
13749                               e.stopEvent();
13750                           }
13751                           fn.call(scope || window, k, e);
13752                           return;
13753                         }
13754                     }
13755                 }else{
13756                     if(k == keyCode){
13757                         if(this.stopEvent){
13758                            e.stopEvent();
13759                         }
13760                         fn.call(scope || window, k, e);
13761                     }
13762                 }
13763             }
13764         };
13765         this.bindings.push(handler);  
13766         },
13767
13768     /**
13769      * Shorthand for adding a single key listener
13770      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13771      * following options:
13772      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13773      * @param {Function} fn The function to call
13774      * @param {Object} scope (optional) The scope of the function
13775      */
13776     on : function(key, fn, scope){
13777         var keyCode, shift, ctrl, alt;
13778         if(typeof key == "object" && !(key instanceof Array)){
13779             keyCode = key.key;
13780             shift = key.shift;
13781             ctrl = key.ctrl;
13782             alt = key.alt;
13783         }else{
13784             keyCode = key;
13785         }
13786         this.addBinding({
13787             key: keyCode,
13788             shift: shift,
13789             ctrl: ctrl,
13790             alt: alt,
13791             fn: fn,
13792             scope: scope
13793         })
13794     },
13795
13796     // private
13797     handleKeyDown : function(e){
13798             if(this.enabled){ //just in case
13799             var b = this.bindings;
13800             for(var i = 0, len = b.length; i < len; i++){
13801                 b[i].call(this, e);
13802             }
13803             }
13804         },
13805         
13806         /**
13807          * Returns true if this KeyMap is enabled
13808          * @return {Boolean} 
13809          */
13810         isEnabled : function(){
13811             return this.enabled;  
13812         },
13813         
13814         /**
13815          * Enables this KeyMap
13816          */
13817         enable: function(){
13818                 if(!this.enabled){
13819                     this.el.on(this.eventName, this.handleKeyDown, this);
13820                     this.enabled = true;
13821                 }
13822         },
13823
13824         /**
13825          * Disable this KeyMap
13826          */
13827         disable: function(){
13828                 if(this.enabled){
13829                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13830                     this.enabled = false;
13831                 }
13832         }
13833 };/*
13834  * Based on:
13835  * Ext JS Library 1.1.1
13836  * Copyright(c) 2006-2007, Ext JS, LLC.
13837  *
13838  * Originally Released Under LGPL - original licence link has changed is not relivant.
13839  *
13840  * Fork - LGPL
13841  * <script type="text/javascript">
13842  */
13843
13844  
13845 /**
13846  * @class Roo.util.TextMetrics
13847  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13848  * wide, in pixels, a given block of text will be.
13849  * @singleton
13850  */
13851 Roo.util.TextMetrics = function(){
13852     var shared;
13853     return {
13854         /**
13855          * Measures the size of the specified text
13856          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13857          * that can affect the size of the rendered text
13858          * @param {String} text The text to measure
13859          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13860          * in order to accurately measure the text height
13861          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13862          */
13863         measure : function(el, text, fixedWidth){
13864             if(!shared){
13865                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13866             }
13867             shared.bind(el);
13868             shared.setFixedWidth(fixedWidth || 'auto');
13869             return shared.getSize(text);
13870         },
13871
13872         /**
13873          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13874          * the overhead of multiple calls to initialize the style properties on each measurement.
13875          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13876          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13877          * in order to accurately measure the text height
13878          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13879          */
13880         createInstance : function(el, fixedWidth){
13881             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13882         }
13883     };
13884 }();
13885
13886  
13887
13888 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13889     var ml = new Roo.Element(document.createElement('div'));
13890     document.body.appendChild(ml.dom);
13891     ml.position('absolute');
13892     ml.setLeftTop(-1000, -1000);
13893     ml.hide();
13894
13895     if(fixedWidth){
13896         ml.setWidth(fixedWidth);
13897     }
13898      
13899     var instance = {
13900         /**
13901          * Returns the size of the specified text based on the internal element's style and width properties
13902          * @memberOf Roo.util.TextMetrics.Instance#
13903          * @param {String} text The text to measure
13904          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13905          */
13906         getSize : function(text){
13907             ml.update(text);
13908             var s = ml.getSize();
13909             ml.update('');
13910             return s;
13911         },
13912
13913         /**
13914          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13915          * that can affect the size of the rendered text
13916          * @memberOf Roo.util.TextMetrics.Instance#
13917          * @param {String/HTMLElement} el The element, dom node or id
13918          */
13919         bind : function(el){
13920             ml.setStyle(
13921                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13922             );
13923         },
13924
13925         /**
13926          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13927          * to set a fixed width in order to accurately measure the text height.
13928          * @memberOf Roo.util.TextMetrics.Instance#
13929          * @param {Number} width The width to set on the element
13930          */
13931         setFixedWidth : function(width){
13932             ml.setWidth(width);
13933         },
13934
13935         /**
13936          * Returns the measured width of the specified text
13937          * @memberOf Roo.util.TextMetrics.Instance#
13938          * @param {String} text The text to measure
13939          * @return {Number} width The width in pixels
13940          */
13941         getWidth : function(text){
13942             ml.dom.style.width = 'auto';
13943             return this.getSize(text).width;
13944         },
13945
13946         /**
13947          * Returns the measured height of the specified text.  For multiline text, be sure to call
13948          * {@link #setFixedWidth} if necessary.
13949          * @memberOf Roo.util.TextMetrics.Instance#
13950          * @param {String} text The text to measure
13951          * @return {Number} height The height in pixels
13952          */
13953         getHeight : function(text){
13954             return this.getSize(text).height;
13955         }
13956     };
13957
13958     instance.bind(bindTo);
13959
13960     return instance;
13961 };
13962
13963 // backwards compat
13964 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975 /**
13976  * @class Roo.state.Provider
13977  * Abstract base class for state provider implementations. This class provides methods
13978  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13979  * Provider interface.
13980  */
13981 Roo.state.Provider = function(){
13982     /**
13983      * @event statechange
13984      * Fires when a state change occurs.
13985      * @param {Provider} this This state provider
13986      * @param {String} key The state key which was changed
13987      * @param {String} value The encoded value for the state
13988      */
13989     this.addEvents({
13990         "statechange": true
13991     });
13992     this.state = {};
13993     Roo.state.Provider.superclass.constructor.call(this);
13994 };
13995 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13996     /**
13997      * Returns the current value for a key
13998      * @param {String} name The key name
13999      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14000      * @return {Mixed} The state data
14001      */
14002     get : function(name, defaultValue){
14003         return typeof this.state[name] == "undefined" ?
14004             defaultValue : this.state[name];
14005     },
14006     
14007     /**
14008      * Clears a value from the state
14009      * @param {String} name The key name
14010      */
14011     clear : function(name){
14012         delete this.state[name];
14013         this.fireEvent("statechange", this, name, null);
14014     },
14015     
14016     /**
14017      * Sets the value for a key
14018      * @param {String} name The key name
14019      * @param {Mixed} value The value to set
14020      */
14021     set : function(name, value){
14022         this.state[name] = value;
14023         this.fireEvent("statechange", this, name, value);
14024     },
14025     
14026     /**
14027      * Decodes a string previously encoded with {@link #encodeValue}.
14028      * @param {String} value The value to decode
14029      * @return {Mixed} The decoded value
14030      */
14031     decodeValue : function(cookie){
14032         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14033         var matches = re.exec(unescape(cookie));
14034         if(!matches || !matches[1]) return; // non state cookie
14035         var type = matches[1];
14036         var v = matches[2];
14037         switch(type){
14038             case "n":
14039                 return parseFloat(v);
14040             case "d":
14041                 return new Date(Date.parse(v));
14042             case "b":
14043                 return (v == "1");
14044             case "a":
14045                 var all = [];
14046                 var values = v.split("^");
14047                 for(var i = 0, len = values.length; i < len; i++){
14048                     all.push(this.decodeValue(values[i]));
14049                 }
14050                 return all;
14051            case "o":
14052                 var all = {};
14053                 var values = v.split("^");
14054                 for(var i = 0, len = values.length; i < len; i++){
14055                     var kv = values[i].split("=");
14056                     all[kv[0]] = this.decodeValue(kv[1]);
14057                 }
14058                 return all;
14059            default:
14060                 return v;
14061         }
14062     },
14063     
14064     /**
14065      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14066      * @param {Mixed} value The value to encode
14067      * @return {String} The encoded value
14068      */
14069     encodeValue : function(v){
14070         var enc;
14071         if(typeof v == "number"){
14072             enc = "n:" + v;
14073         }else if(typeof v == "boolean"){
14074             enc = "b:" + (v ? "1" : "0");
14075         }else if(v instanceof Date){
14076             enc = "d:" + v.toGMTString();
14077         }else if(v instanceof Array){
14078             var flat = "";
14079             for(var i = 0, len = v.length; i < len; i++){
14080                 flat += this.encodeValue(v[i]);
14081                 if(i != len-1) flat += "^";
14082             }
14083             enc = "a:" + flat;
14084         }else if(typeof v == "object"){
14085             var flat = "";
14086             for(var key in v){
14087                 if(typeof v[key] != "function"){
14088                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14089                 }
14090             }
14091             enc = "o:" + flat.substring(0, flat.length-1);
14092         }else{
14093             enc = "s:" + v;
14094         }
14095         return escape(enc);        
14096     }
14097 });
14098
14099 /*
14100  * Based on:
14101  * Ext JS Library 1.1.1
14102  * Copyright(c) 2006-2007, Ext JS, LLC.
14103  *
14104  * Originally Released Under LGPL - original licence link has changed is not relivant.
14105  *
14106  * Fork - LGPL
14107  * <script type="text/javascript">
14108  */
14109 /**
14110  * @class Roo.state.Manager
14111  * This is the global state manager. By default all components that are "state aware" check this class
14112  * for state information if you don't pass them a custom state provider. In order for this class
14113  * to be useful, it must be initialized with a provider when your application initializes.
14114  <pre><code>
14115 // in your initialization function
14116 init : function(){
14117    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14118    ...
14119    // supposed you have a {@link Roo.BorderLayout}
14120    var layout = new Roo.BorderLayout(...);
14121    layout.restoreState();
14122    // or a {Roo.BasicDialog}
14123    var dialog = new Roo.BasicDialog(...);
14124    dialog.restoreState();
14125  </code></pre>
14126  * @singleton
14127  */
14128 Roo.state.Manager = function(){
14129     var provider = new Roo.state.Provider();
14130     
14131     return {
14132         /**
14133          * Configures the default state provider for your application
14134          * @param {Provider} stateProvider The state provider to set
14135          */
14136         setProvider : function(stateProvider){
14137             provider = stateProvider;
14138         },
14139         
14140         /**
14141          * Returns the current value for a key
14142          * @param {String} name The key name
14143          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14144          * @return {Mixed} The state data
14145          */
14146         get : function(key, defaultValue){
14147             return provider.get(key, defaultValue);
14148         },
14149         
14150         /**
14151          * Sets the value for a key
14152          * @param {String} name The key name
14153          * @param {Mixed} value The state data
14154          */
14155          set : function(key, value){
14156             provider.set(key, value);
14157         },
14158         
14159         /**
14160          * Clears a value from the state
14161          * @param {String} name The key name
14162          */
14163         clear : function(key){
14164             provider.clear(key);
14165         },
14166         
14167         /**
14168          * Gets the currently configured state provider
14169          * @return {Provider} The state provider
14170          */
14171         getProvider : function(){
14172             return provider;
14173         }
14174     };
14175 }();
14176 /*
14177  * Based on:
14178  * Ext JS Library 1.1.1
14179  * Copyright(c) 2006-2007, Ext JS, LLC.
14180  *
14181  * Originally Released Under LGPL - original licence link has changed is not relivant.
14182  *
14183  * Fork - LGPL
14184  * <script type="text/javascript">
14185  */
14186 /**
14187  * @class Roo.state.CookieProvider
14188  * @extends Roo.state.Provider
14189  * The default Provider implementation which saves state via cookies.
14190  * <br />Usage:
14191  <pre><code>
14192    var cp = new Roo.state.CookieProvider({
14193        path: "/cgi-bin/",
14194        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14195        domain: "roojs.com"
14196    })
14197    Roo.state.Manager.setProvider(cp);
14198  </code></pre>
14199  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14200  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14201  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14202  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14203  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14204  * domain the page is running on including the 'www' like 'www.roojs.com')
14205  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14206  * @constructor
14207  * Create a new CookieProvider
14208  * @param {Object} config The configuration object
14209  */
14210 Roo.state.CookieProvider = function(config){
14211     Roo.state.CookieProvider.superclass.constructor.call(this);
14212     this.path = "/";
14213     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14214     this.domain = null;
14215     this.secure = false;
14216     Roo.apply(this, config);
14217     this.state = this.readCookies();
14218 };
14219
14220 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14221     // private
14222     set : function(name, value){
14223         if(typeof value == "undefined" || value === null){
14224             this.clear(name);
14225             return;
14226         }
14227         this.setCookie(name, value);
14228         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14229     },
14230
14231     // private
14232     clear : function(name){
14233         this.clearCookie(name);
14234         Roo.state.CookieProvider.superclass.clear.call(this, name);
14235     },
14236
14237     // private
14238     readCookies : function(){
14239         var cookies = {};
14240         var c = document.cookie + ";";
14241         var re = /\s?(.*?)=(.*?);/g;
14242         var matches;
14243         while((matches = re.exec(c)) != null){
14244             var name = matches[1];
14245             var value = matches[2];
14246             if(name && name.substring(0,3) == "ys-"){
14247                 cookies[name.substr(3)] = this.decodeValue(value);
14248             }
14249         }
14250         return cookies;
14251     },
14252
14253     // private
14254     setCookie : function(name, value){
14255         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14256            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14257            ((this.path == null) ? "" : ("; path=" + this.path)) +
14258            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14259            ((this.secure == true) ? "; secure" : "");
14260     },
14261
14262     // private
14263     clearCookie : function(name){
14264         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14265            ((this.path == null) ? "" : ("; path=" + this.path)) +
14266            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14267            ((this.secure == true) ? "; secure" : "");
14268     }
14269 });/*
14270  * Based on:
14271  * Ext JS Library 1.1.1
14272  * Copyright(c) 2006-2007, Ext JS, LLC.
14273  *
14274  * Originally Released Under LGPL - original licence link has changed is not relivant.
14275  *
14276  * Fork - LGPL
14277  * <script type="text/javascript">
14278  */
14279
14280
14281
14282 /*
14283  * These classes are derivatives of the similarly named classes in the YUI Library.
14284  * The original license:
14285  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14286  * Code licensed under the BSD License:
14287  * http://developer.yahoo.net/yui/license.txt
14288  */
14289
14290 (function() {
14291
14292 var Event=Roo.EventManager;
14293 var Dom=Roo.lib.Dom;
14294
14295 /**
14296  * @class Roo.dd.DragDrop
14297  * Defines the interface and base operation of items that that can be
14298  * dragged or can be drop targets.  It was designed to be extended, overriding
14299  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14300  * Up to three html elements can be associated with a DragDrop instance:
14301  * <ul>
14302  * <li>linked element: the element that is passed into the constructor.
14303  * This is the element which defines the boundaries for interaction with
14304  * other DragDrop objects.</li>
14305  * <li>handle element(s): The drag operation only occurs if the element that
14306  * was clicked matches a handle element.  By default this is the linked
14307  * element, but there are times that you will want only a portion of the
14308  * linked element to initiate the drag operation, and the setHandleElId()
14309  * method provides a way to define this.</li>
14310  * <li>drag element: this represents the element that would be moved along
14311  * with the cursor during a drag operation.  By default, this is the linked
14312  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14313  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14314  * </li>
14315  * </ul>
14316  * This class should not be instantiated until the onload event to ensure that
14317  * the associated elements are available.
14318  * The following would define a DragDrop obj that would interact with any
14319  * other DragDrop obj in the "group1" group:
14320  * <pre>
14321  *  dd = new Roo.dd.DragDrop("div1", "group1");
14322  * </pre>
14323  * Since none of the event handlers have been implemented, nothing would
14324  * actually happen if you were to run the code above.  Normally you would
14325  * override this class or one of the default implementations, but you can
14326  * also override the methods you want on an instance of the class...
14327  * <pre>
14328  *  dd.onDragDrop = function(e, id) {
14329  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14330  *  }
14331  * </pre>
14332  * @constructor
14333  * @param {String} id of the element that is linked to this instance
14334  * @param {String} sGroup the group of related DragDrop objects
14335  * @param {object} config an object containing configurable attributes
14336  *                Valid properties for DragDrop:
14337  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14338  */
14339 Roo.dd.DragDrop = function(id, sGroup, config) {
14340     if (id) {
14341         this.init(id, sGroup, config);
14342     }
14343 };
14344
14345 Roo.dd.DragDrop.prototype = {
14346
14347     /**
14348      * The id of the element associated with this object.  This is what we
14349      * refer to as the "linked element" because the size and position of
14350      * this element is used to determine when the drag and drop objects have
14351      * interacted.
14352      * @property id
14353      * @type String
14354      */
14355     id: null,
14356
14357     /**
14358      * Configuration attributes passed into the constructor
14359      * @property config
14360      * @type object
14361      */
14362     config: null,
14363
14364     /**
14365      * The id of the element that will be dragged.  By default this is same
14366      * as the linked element , but could be changed to another element. Ex:
14367      * Roo.dd.DDProxy
14368      * @property dragElId
14369      * @type String
14370      * @private
14371      */
14372     dragElId: null,
14373
14374     /**
14375      * the id of the element that initiates the drag operation.  By default
14376      * this is the linked element, but could be changed to be a child of this
14377      * element.  This lets us do things like only starting the drag when the
14378      * header element within the linked html element is clicked.
14379      * @property handleElId
14380      * @type String
14381      * @private
14382      */
14383     handleElId: null,
14384
14385     /**
14386      * An associative array of HTML tags that will be ignored if clicked.
14387      * @property invalidHandleTypes
14388      * @type {string: string}
14389      */
14390     invalidHandleTypes: null,
14391
14392     /**
14393      * An associative array of ids for elements that will be ignored if clicked
14394      * @property invalidHandleIds
14395      * @type {string: string}
14396      */
14397     invalidHandleIds: null,
14398
14399     /**
14400      * An indexted array of css class names for elements that will be ignored
14401      * if clicked.
14402      * @property invalidHandleClasses
14403      * @type string[]
14404      */
14405     invalidHandleClasses: null,
14406
14407     /**
14408      * The linked element's absolute X position at the time the drag was
14409      * started
14410      * @property startPageX
14411      * @type int
14412      * @private
14413      */
14414     startPageX: 0,
14415
14416     /**
14417      * The linked element's absolute X position at the time the drag was
14418      * started
14419      * @property startPageY
14420      * @type int
14421      * @private
14422      */
14423     startPageY: 0,
14424
14425     /**
14426      * The group defines a logical collection of DragDrop objects that are
14427      * related.  Instances only get events when interacting with other
14428      * DragDrop object in the same group.  This lets us define multiple
14429      * groups using a single DragDrop subclass if we want.
14430      * @property groups
14431      * @type {string: string}
14432      */
14433     groups: null,
14434
14435     /**
14436      * Individual drag/drop instances can be locked.  This will prevent
14437      * onmousedown start drag.
14438      * @property locked
14439      * @type boolean
14440      * @private
14441      */
14442     locked: false,
14443
14444     /**
14445      * Lock this instance
14446      * @method lock
14447      */
14448     lock: function() { this.locked = true; },
14449
14450     /**
14451      * Unlock this instace
14452      * @method unlock
14453      */
14454     unlock: function() { this.locked = false; },
14455
14456     /**
14457      * By default, all insances can be a drop target.  This can be disabled by
14458      * setting isTarget to false.
14459      * @method isTarget
14460      * @type boolean
14461      */
14462     isTarget: true,
14463
14464     /**
14465      * The padding configured for this drag and drop object for calculating
14466      * the drop zone intersection with this object.
14467      * @method padding
14468      * @type int[]
14469      */
14470     padding: null,
14471
14472     /**
14473      * Cached reference to the linked element
14474      * @property _domRef
14475      * @private
14476      */
14477     _domRef: null,
14478
14479     /**
14480      * Internal typeof flag
14481      * @property __ygDragDrop
14482      * @private
14483      */
14484     __ygDragDrop: true,
14485
14486     /**
14487      * Set to true when horizontal contraints are applied
14488      * @property constrainX
14489      * @type boolean
14490      * @private
14491      */
14492     constrainX: false,
14493
14494     /**
14495      * Set to true when vertical contraints are applied
14496      * @property constrainY
14497      * @type boolean
14498      * @private
14499      */
14500     constrainY: false,
14501
14502     /**
14503      * The left constraint
14504      * @property minX
14505      * @type int
14506      * @private
14507      */
14508     minX: 0,
14509
14510     /**
14511      * The right constraint
14512      * @property maxX
14513      * @type int
14514      * @private
14515      */
14516     maxX: 0,
14517
14518     /**
14519      * The up constraint
14520      * @property minY
14521      * @type int
14522      * @type int
14523      * @private
14524      */
14525     minY: 0,
14526
14527     /**
14528      * The down constraint
14529      * @property maxY
14530      * @type int
14531      * @private
14532      */
14533     maxY: 0,
14534
14535     /**
14536      * Maintain offsets when we resetconstraints.  Set to true when you want
14537      * the position of the element relative to its parent to stay the same
14538      * when the page changes
14539      *
14540      * @property maintainOffset
14541      * @type boolean
14542      */
14543     maintainOffset: false,
14544
14545     /**
14546      * Array of pixel locations the element will snap to if we specified a
14547      * horizontal graduation/interval.  This array is generated automatically
14548      * when you define a tick interval.
14549      * @property xTicks
14550      * @type int[]
14551      */
14552     xTicks: null,
14553
14554     /**
14555      * Array of pixel locations the element will snap to if we specified a
14556      * vertical graduation/interval.  This array is generated automatically
14557      * when you define a tick interval.
14558      * @property yTicks
14559      * @type int[]
14560      */
14561     yTicks: null,
14562
14563     /**
14564      * By default the drag and drop instance will only respond to the primary
14565      * button click (left button for a right-handed mouse).  Set to true to
14566      * allow drag and drop to start with any mouse click that is propogated
14567      * by the browser
14568      * @property primaryButtonOnly
14569      * @type boolean
14570      */
14571     primaryButtonOnly: true,
14572
14573     /**
14574      * The availabe property is false until the linked dom element is accessible.
14575      * @property available
14576      * @type boolean
14577      */
14578     available: false,
14579
14580     /**
14581      * By default, drags can only be initiated if the mousedown occurs in the
14582      * region the linked element is.  This is done in part to work around a
14583      * bug in some browsers that mis-report the mousedown if the previous
14584      * mouseup happened outside of the window.  This property is set to true
14585      * if outer handles are defined.
14586      *
14587      * @property hasOuterHandles
14588      * @type boolean
14589      * @default false
14590      */
14591     hasOuterHandles: false,
14592
14593     /**
14594      * Code that executes immediately before the startDrag event
14595      * @method b4StartDrag
14596      * @private
14597      */
14598     b4StartDrag: function(x, y) { },
14599
14600     /**
14601      * Abstract method called after a drag/drop object is clicked
14602      * and the drag or mousedown time thresholds have beeen met.
14603      * @method startDrag
14604      * @param {int} X click location
14605      * @param {int} Y click location
14606      */
14607     startDrag: function(x, y) { /* override this */ },
14608
14609     /**
14610      * Code that executes immediately before the onDrag event
14611      * @method b4Drag
14612      * @private
14613      */
14614     b4Drag: function(e) { },
14615
14616     /**
14617      * Abstract method called during the onMouseMove event while dragging an
14618      * object.
14619      * @method onDrag
14620      * @param {Event} e the mousemove event
14621      */
14622     onDrag: function(e) { /* override this */ },
14623
14624     /**
14625      * Abstract method called when this element fist begins hovering over
14626      * another DragDrop obj
14627      * @method onDragEnter
14628      * @param {Event} e the mousemove event
14629      * @param {String|DragDrop[]} id In POINT mode, the element
14630      * id this is hovering over.  In INTERSECT mode, an array of one or more
14631      * dragdrop items being hovered over.
14632      */
14633     onDragEnter: function(e, id) { /* override this */ },
14634
14635     /**
14636      * Code that executes immediately before the onDragOver event
14637      * @method b4DragOver
14638      * @private
14639      */
14640     b4DragOver: function(e) { },
14641
14642     /**
14643      * Abstract method called when this element is hovering over another
14644      * DragDrop obj
14645      * @method onDragOver
14646      * @param {Event} e the mousemove event
14647      * @param {String|DragDrop[]} id In POINT mode, the element
14648      * id this is hovering over.  In INTERSECT mode, an array of dd items
14649      * being hovered over.
14650      */
14651     onDragOver: function(e, id) { /* override this */ },
14652
14653     /**
14654      * Code that executes immediately before the onDragOut event
14655      * @method b4DragOut
14656      * @private
14657      */
14658     b4DragOut: function(e) { },
14659
14660     /**
14661      * Abstract method called when we are no longer hovering over an element
14662      * @method onDragOut
14663      * @param {Event} e the mousemove event
14664      * @param {String|DragDrop[]} id In POINT mode, the element
14665      * id this was hovering over.  In INTERSECT mode, an array of dd items
14666      * that the mouse is no longer over.
14667      */
14668     onDragOut: function(e, id) { /* override this */ },
14669
14670     /**
14671      * Code that executes immediately before the onDragDrop event
14672      * @method b4DragDrop
14673      * @private
14674      */
14675     b4DragDrop: function(e) { },
14676
14677     /**
14678      * Abstract method called when this item is dropped on another DragDrop
14679      * obj
14680      * @method onDragDrop
14681      * @param {Event} e the mouseup event
14682      * @param {String|DragDrop[]} id In POINT mode, the element
14683      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14684      * was dropped on.
14685      */
14686     onDragDrop: function(e, id) { /* override this */ },
14687
14688     /**
14689      * Abstract method called when this item is dropped on an area with no
14690      * drop target
14691      * @method onInvalidDrop
14692      * @param {Event} e the mouseup event
14693      */
14694     onInvalidDrop: function(e) { /* override this */ },
14695
14696     /**
14697      * Code that executes immediately before the endDrag event
14698      * @method b4EndDrag
14699      * @private
14700      */
14701     b4EndDrag: function(e) { },
14702
14703     /**
14704      * Fired when we are done dragging the object
14705      * @method endDrag
14706      * @param {Event} e the mouseup event
14707      */
14708     endDrag: function(e) { /* override this */ },
14709
14710     /**
14711      * Code executed immediately before the onMouseDown event
14712      * @method b4MouseDown
14713      * @param {Event} e the mousedown event
14714      * @private
14715      */
14716     b4MouseDown: function(e) {  },
14717
14718     /**
14719      * Event handler that fires when a drag/drop obj gets a mousedown
14720      * @method onMouseDown
14721      * @param {Event} e the mousedown event
14722      */
14723     onMouseDown: function(e) { /* override this */ },
14724
14725     /**
14726      * Event handler that fires when a drag/drop obj gets a mouseup
14727      * @method onMouseUp
14728      * @param {Event} e the mouseup event
14729      */
14730     onMouseUp: function(e) { /* override this */ },
14731
14732     /**
14733      * Override the onAvailable method to do what is needed after the initial
14734      * position was determined.
14735      * @method onAvailable
14736      */
14737     onAvailable: function () {
14738     },
14739
14740     /*
14741      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14742      * @type Object
14743      */
14744     defaultPadding : {left:0, right:0, top:0, bottom:0},
14745
14746     /*
14747      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14748  *
14749  * Usage:
14750  <pre><code>
14751  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14752                 { dragElId: "existingProxyDiv" });
14753  dd.startDrag = function(){
14754      this.constrainTo("parent-id");
14755  };
14756  </code></pre>
14757  * Or you can initalize it using the {@link Roo.Element} object:
14758  <pre><code>
14759  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14760      startDrag : function(){
14761          this.constrainTo("parent-id");
14762      }
14763  });
14764  </code></pre>
14765      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14766      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14767      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14768      * an object containing the sides to pad. For example: {right:10, bottom:10}
14769      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14770      */
14771     constrainTo : function(constrainTo, pad, inContent){
14772         if(typeof pad == "number"){
14773             pad = {left: pad, right:pad, top:pad, bottom:pad};
14774         }
14775         pad = pad || this.defaultPadding;
14776         var b = Roo.get(this.getEl()).getBox();
14777         var ce = Roo.get(constrainTo);
14778         var s = ce.getScroll();
14779         var c, cd = ce.dom;
14780         if(cd == document.body){
14781             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14782         }else{
14783             xy = ce.getXY();
14784             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14785         }
14786
14787
14788         var topSpace = b.y - c.y;
14789         var leftSpace = b.x - c.x;
14790
14791         this.resetConstraints();
14792         this.setXConstraint(leftSpace - (pad.left||0), // left
14793                 c.width - leftSpace - b.width - (pad.right||0) //right
14794         );
14795         this.setYConstraint(topSpace - (pad.top||0), //top
14796                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14797         );
14798     },
14799
14800     /**
14801      * Returns a reference to the linked element
14802      * @method getEl
14803      * @return {HTMLElement} the html element
14804      */
14805     getEl: function() {
14806         if (!this._domRef) {
14807             this._domRef = Roo.getDom(this.id);
14808         }
14809
14810         return this._domRef;
14811     },
14812
14813     /**
14814      * Returns a reference to the actual element to drag.  By default this is
14815      * the same as the html element, but it can be assigned to another
14816      * element. An example of this can be found in Roo.dd.DDProxy
14817      * @method getDragEl
14818      * @return {HTMLElement} the html element
14819      */
14820     getDragEl: function() {
14821         return Roo.getDom(this.dragElId);
14822     },
14823
14824     /**
14825      * Sets up the DragDrop object.  Must be called in the constructor of any
14826      * Roo.dd.DragDrop subclass
14827      * @method init
14828      * @param id the id of the linked element
14829      * @param {String} sGroup the group of related items
14830      * @param {object} config configuration attributes
14831      */
14832     init: function(id, sGroup, config) {
14833         this.initTarget(id, sGroup, config);
14834         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14835         // Event.on(this.id, "selectstart", Event.preventDefault);
14836     },
14837
14838     /**
14839      * Initializes Targeting functionality only... the object does not
14840      * get a mousedown handler.
14841      * @method initTarget
14842      * @param id the id of the linked element
14843      * @param {String} sGroup the group of related items
14844      * @param {object} config configuration attributes
14845      */
14846     initTarget: function(id, sGroup, config) {
14847
14848         // configuration attributes
14849         this.config = config || {};
14850
14851         // create a local reference to the drag and drop manager
14852         this.DDM = Roo.dd.DDM;
14853         // initialize the groups array
14854         this.groups = {};
14855
14856         // assume that we have an element reference instead of an id if the
14857         // parameter is not a string
14858         if (typeof id !== "string") {
14859             id = Roo.id(id);
14860         }
14861
14862         // set the id
14863         this.id = id;
14864
14865         // add to an interaction group
14866         this.addToGroup((sGroup) ? sGroup : "default");
14867
14868         // We don't want to register this as the handle with the manager
14869         // so we just set the id rather than calling the setter.
14870         this.handleElId = id;
14871
14872         // the linked element is the element that gets dragged by default
14873         this.setDragElId(id);
14874
14875         // by default, clicked anchors will not start drag operations.
14876         this.invalidHandleTypes = { A: "A" };
14877         this.invalidHandleIds = {};
14878         this.invalidHandleClasses = [];
14879
14880         this.applyConfig();
14881
14882         this.handleOnAvailable();
14883     },
14884
14885     /**
14886      * Applies the configuration parameters that were passed into the constructor.
14887      * This is supposed to happen at each level through the inheritance chain.  So
14888      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14889      * DragDrop in order to get all of the parameters that are available in
14890      * each object.
14891      * @method applyConfig
14892      */
14893     applyConfig: function() {
14894
14895         // configurable properties:
14896         //    padding, isTarget, maintainOffset, primaryButtonOnly
14897         this.padding           = this.config.padding || [0, 0, 0, 0];
14898         this.isTarget          = (this.config.isTarget !== false);
14899         this.maintainOffset    = (this.config.maintainOffset);
14900         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14901
14902     },
14903
14904     /**
14905      * Executed when the linked element is available
14906      * @method handleOnAvailable
14907      * @private
14908      */
14909     handleOnAvailable: function() {
14910         this.available = true;
14911         this.resetConstraints();
14912         this.onAvailable();
14913     },
14914
14915      /**
14916      * Configures the padding for the target zone in px.  Effectively expands
14917      * (or reduces) the virtual object size for targeting calculations.
14918      * Supports css-style shorthand; if only one parameter is passed, all sides
14919      * will have that padding, and if only two are passed, the top and bottom
14920      * will have the first param, the left and right the second.
14921      * @method setPadding
14922      * @param {int} iTop    Top pad
14923      * @param {int} iRight  Right pad
14924      * @param {int} iBot    Bot pad
14925      * @param {int} iLeft   Left pad
14926      */
14927     setPadding: function(iTop, iRight, iBot, iLeft) {
14928         // this.padding = [iLeft, iRight, iTop, iBot];
14929         if (!iRight && 0 !== iRight) {
14930             this.padding = [iTop, iTop, iTop, iTop];
14931         } else if (!iBot && 0 !== iBot) {
14932             this.padding = [iTop, iRight, iTop, iRight];
14933         } else {
14934             this.padding = [iTop, iRight, iBot, iLeft];
14935         }
14936     },
14937
14938     /**
14939      * Stores the initial placement of the linked element.
14940      * @method setInitialPosition
14941      * @param {int} diffX   the X offset, default 0
14942      * @param {int} diffY   the Y offset, default 0
14943      */
14944     setInitPosition: function(diffX, diffY) {
14945         var el = this.getEl();
14946
14947         if (!this.DDM.verifyEl(el)) {
14948             return;
14949         }
14950
14951         var dx = diffX || 0;
14952         var dy = diffY || 0;
14953
14954         var p = Dom.getXY( el );
14955
14956         this.initPageX = p[0] - dx;
14957         this.initPageY = p[1] - dy;
14958
14959         this.lastPageX = p[0];
14960         this.lastPageY = p[1];
14961
14962
14963         this.setStartPosition(p);
14964     },
14965
14966     /**
14967      * Sets the start position of the element.  This is set when the obj
14968      * is initialized, the reset when a drag is started.
14969      * @method setStartPosition
14970      * @param pos current position (from previous lookup)
14971      * @private
14972      */
14973     setStartPosition: function(pos) {
14974         var p = pos || Dom.getXY( this.getEl() );
14975         this.deltaSetXY = null;
14976
14977         this.startPageX = p[0];
14978         this.startPageY = p[1];
14979     },
14980
14981     /**
14982      * Add this instance to a group of related drag/drop objects.  All
14983      * instances belong to at least one group, and can belong to as many
14984      * groups as needed.
14985      * @method addToGroup
14986      * @param sGroup {string} the name of the group
14987      */
14988     addToGroup: function(sGroup) {
14989         this.groups[sGroup] = true;
14990         this.DDM.regDragDrop(this, sGroup);
14991     },
14992
14993     /**
14994      * Remove's this instance from the supplied interaction group
14995      * @method removeFromGroup
14996      * @param {string}  sGroup  The group to drop
14997      */
14998     removeFromGroup: function(sGroup) {
14999         if (this.groups[sGroup]) {
15000             delete this.groups[sGroup];
15001         }
15002
15003         this.DDM.removeDDFromGroup(this, sGroup);
15004     },
15005
15006     /**
15007      * Allows you to specify that an element other than the linked element
15008      * will be moved with the cursor during a drag
15009      * @method setDragElId
15010      * @param id {string} the id of the element that will be used to initiate the drag
15011      */
15012     setDragElId: function(id) {
15013         this.dragElId = id;
15014     },
15015
15016     /**
15017      * Allows you to specify a child of the linked element that should be
15018      * used to initiate the drag operation.  An example of this would be if
15019      * you have a content div with text and links.  Clicking anywhere in the
15020      * content area would normally start the drag operation.  Use this method
15021      * to specify that an element inside of the content div is the element
15022      * that starts the drag operation.
15023      * @method setHandleElId
15024      * @param id {string} the id of the element that will be used to
15025      * initiate the drag.
15026      */
15027     setHandleElId: function(id) {
15028         if (typeof id !== "string") {
15029             id = Roo.id(id);
15030         }
15031         this.handleElId = id;
15032         this.DDM.regHandle(this.id, id);
15033     },
15034
15035     /**
15036      * Allows you to set an element outside of the linked element as a drag
15037      * handle
15038      * @method setOuterHandleElId
15039      * @param id the id of the element that will be used to initiate the drag
15040      */
15041     setOuterHandleElId: function(id) {
15042         if (typeof id !== "string") {
15043             id = Roo.id(id);
15044         }
15045         Event.on(id, "mousedown",
15046                 this.handleMouseDown, this);
15047         this.setHandleElId(id);
15048
15049         this.hasOuterHandles = true;
15050     },
15051
15052     /**
15053      * Remove all drag and drop hooks for this element
15054      * @method unreg
15055      */
15056     unreg: function() {
15057         Event.un(this.id, "mousedown",
15058                 this.handleMouseDown);
15059         this._domRef = null;
15060         this.DDM._remove(this);
15061     },
15062
15063     destroy : function(){
15064         this.unreg();
15065     },
15066
15067     /**
15068      * Returns true if this instance is locked, or the drag drop mgr is locked
15069      * (meaning that all drag/drop is disabled on the page.)
15070      * @method isLocked
15071      * @return {boolean} true if this obj or all drag/drop is locked, else
15072      * false
15073      */
15074     isLocked: function() {
15075         return (this.DDM.isLocked() || this.locked);
15076     },
15077
15078     /**
15079      * Fired when this object is clicked
15080      * @method handleMouseDown
15081      * @param {Event} e
15082      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15083      * @private
15084      */
15085     handleMouseDown: function(e, oDD){
15086         if (this.primaryButtonOnly && e.button != 0) {
15087             return;
15088         }
15089
15090         if (this.isLocked()) {
15091             return;
15092         }
15093
15094         this.DDM.refreshCache(this.groups);
15095
15096         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15097         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15098         } else {
15099             if (this.clickValidator(e)) {
15100
15101                 // set the initial element position
15102                 this.setStartPosition();
15103
15104
15105                 this.b4MouseDown(e);
15106                 this.onMouseDown(e);
15107
15108                 this.DDM.handleMouseDown(e, this);
15109
15110                 this.DDM.stopEvent(e);
15111             } else {
15112
15113
15114             }
15115         }
15116     },
15117
15118     clickValidator: function(e) {
15119         var target = e.getTarget();
15120         return ( this.isValidHandleChild(target) &&
15121                     (this.id == this.handleElId ||
15122                         this.DDM.handleWasClicked(target, this.id)) );
15123     },
15124
15125     /**
15126      * Allows you to specify a tag name that should not start a drag operation
15127      * when clicked.  This is designed to facilitate embedding links within a
15128      * drag handle that do something other than start the drag.
15129      * @method addInvalidHandleType
15130      * @param {string} tagName the type of element to exclude
15131      */
15132     addInvalidHandleType: function(tagName) {
15133         var type = tagName.toUpperCase();
15134         this.invalidHandleTypes[type] = type;
15135     },
15136
15137     /**
15138      * Lets you to specify an element id for a child of a drag handle
15139      * that should not initiate a drag
15140      * @method addInvalidHandleId
15141      * @param {string} id the element id of the element you wish to ignore
15142      */
15143     addInvalidHandleId: function(id) {
15144         if (typeof id !== "string") {
15145             id = Roo.id(id);
15146         }
15147         this.invalidHandleIds[id] = id;
15148     },
15149
15150     /**
15151      * Lets you specify a css class of elements that will not initiate a drag
15152      * @method addInvalidHandleClass
15153      * @param {string} cssClass the class of the elements you wish to ignore
15154      */
15155     addInvalidHandleClass: function(cssClass) {
15156         this.invalidHandleClasses.push(cssClass);
15157     },
15158
15159     /**
15160      * Unsets an excluded tag name set by addInvalidHandleType
15161      * @method removeInvalidHandleType
15162      * @param {string} tagName the type of element to unexclude
15163      */
15164     removeInvalidHandleType: function(tagName) {
15165         var type = tagName.toUpperCase();
15166         // this.invalidHandleTypes[type] = null;
15167         delete this.invalidHandleTypes[type];
15168     },
15169
15170     /**
15171      * Unsets an invalid handle id
15172      * @method removeInvalidHandleId
15173      * @param {string} id the id of the element to re-enable
15174      */
15175     removeInvalidHandleId: function(id) {
15176         if (typeof id !== "string") {
15177             id = Roo.id(id);
15178         }
15179         delete this.invalidHandleIds[id];
15180     },
15181
15182     /**
15183      * Unsets an invalid css class
15184      * @method removeInvalidHandleClass
15185      * @param {string} cssClass the class of the element(s) you wish to
15186      * re-enable
15187      */
15188     removeInvalidHandleClass: function(cssClass) {
15189         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15190             if (this.invalidHandleClasses[i] == cssClass) {
15191                 delete this.invalidHandleClasses[i];
15192             }
15193         }
15194     },
15195
15196     /**
15197      * Checks the tag exclusion list to see if this click should be ignored
15198      * @method isValidHandleChild
15199      * @param {HTMLElement} node the HTMLElement to evaluate
15200      * @return {boolean} true if this is a valid tag type, false if not
15201      */
15202     isValidHandleChild: function(node) {
15203
15204         var valid = true;
15205         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15206         var nodeName;
15207         try {
15208             nodeName = node.nodeName.toUpperCase();
15209         } catch(e) {
15210             nodeName = node.nodeName;
15211         }
15212         valid = valid && !this.invalidHandleTypes[nodeName];
15213         valid = valid && !this.invalidHandleIds[node.id];
15214
15215         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15216             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15217         }
15218
15219
15220         return valid;
15221
15222     },
15223
15224     /**
15225      * Create the array of horizontal tick marks if an interval was specified
15226      * in setXConstraint().
15227      * @method setXTicks
15228      * @private
15229      */
15230     setXTicks: function(iStartX, iTickSize) {
15231         this.xTicks = [];
15232         this.xTickSize = iTickSize;
15233
15234         var tickMap = {};
15235
15236         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15237             if (!tickMap[i]) {
15238                 this.xTicks[this.xTicks.length] = i;
15239                 tickMap[i] = true;
15240             }
15241         }
15242
15243         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15244             if (!tickMap[i]) {
15245                 this.xTicks[this.xTicks.length] = i;
15246                 tickMap[i] = true;
15247             }
15248         }
15249
15250         this.xTicks.sort(this.DDM.numericSort) ;
15251     },
15252
15253     /**
15254      * Create the array of vertical tick marks if an interval was specified in
15255      * setYConstraint().
15256      * @method setYTicks
15257      * @private
15258      */
15259     setYTicks: function(iStartY, iTickSize) {
15260         this.yTicks = [];
15261         this.yTickSize = iTickSize;
15262
15263         var tickMap = {};
15264
15265         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15266             if (!tickMap[i]) {
15267                 this.yTicks[this.yTicks.length] = i;
15268                 tickMap[i] = true;
15269             }
15270         }
15271
15272         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15273             if (!tickMap[i]) {
15274                 this.yTicks[this.yTicks.length] = i;
15275                 tickMap[i] = true;
15276             }
15277         }
15278
15279         this.yTicks.sort(this.DDM.numericSort) ;
15280     },
15281
15282     /**
15283      * By default, the element can be dragged any place on the screen.  Use
15284      * this method to limit the horizontal travel of the element.  Pass in
15285      * 0,0 for the parameters if you want to lock the drag to the y axis.
15286      * @method setXConstraint
15287      * @param {int} iLeft the number of pixels the element can move to the left
15288      * @param {int} iRight the number of pixels the element can move to the
15289      * right
15290      * @param {int} iTickSize optional parameter for specifying that the
15291      * element
15292      * should move iTickSize pixels at a time.
15293      */
15294     setXConstraint: function(iLeft, iRight, iTickSize) {
15295         this.leftConstraint = iLeft;
15296         this.rightConstraint = iRight;
15297
15298         this.minX = this.initPageX - iLeft;
15299         this.maxX = this.initPageX + iRight;
15300         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15301
15302         this.constrainX = true;
15303     },
15304
15305     /**
15306      * Clears any constraints applied to this instance.  Also clears ticks
15307      * since they can't exist independent of a constraint at this time.
15308      * @method clearConstraints
15309      */
15310     clearConstraints: function() {
15311         this.constrainX = false;
15312         this.constrainY = false;
15313         this.clearTicks();
15314     },
15315
15316     /**
15317      * Clears any tick interval defined for this instance
15318      * @method clearTicks
15319      */
15320     clearTicks: function() {
15321         this.xTicks = null;
15322         this.yTicks = null;
15323         this.xTickSize = 0;
15324         this.yTickSize = 0;
15325     },
15326
15327     /**
15328      * By default, the element can be dragged any place on the screen.  Set
15329      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15330      * parameters if you want to lock the drag to the x axis.
15331      * @method setYConstraint
15332      * @param {int} iUp the number of pixels the element can move up
15333      * @param {int} iDown the number of pixels the element can move down
15334      * @param {int} iTickSize optional parameter for specifying that the
15335      * element should move iTickSize pixels at a time.
15336      */
15337     setYConstraint: function(iUp, iDown, iTickSize) {
15338         this.topConstraint = iUp;
15339         this.bottomConstraint = iDown;
15340
15341         this.minY = this.initPageY - iUp;
15342         this.maxY = this.initPageY + iDown;
15343         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15344
15345         this.constrainY = true;
15346
15347     },
15348
15349     /**
15350      * resetConstraints must be called if you manually reposition a dd element.
15351      * @method resetConstraints
15352      * @param {boolean} maintainOffset
15353      */
15354     resetConstraints: function() {
15355
15356
15357         // Maintain offsets if necessary
15358         if (this.initPageX || this.initPageX === 0) {
15359             // figure out how much this thing has moved
15360             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15361             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15362
15363             this.setInitPosition(dx, dy);
15364
15365         // This is the first time we have detected the element's position
15366         } else {
15367             this.setInitPosition();
15368         }
15369
15370         if (this.constrainX) {
15371             this.setXConstraint( this.leftConstraint,
15372                                  this.rightConstraint,
15373                                  this.xTickSize        );
15374         }
15375
15376         if (this.constrainY) {
15377             this.setYConstraint( this.topConstraint,
15378                                  this.bottomConstraint,
15379                                  this.yTickSize         );
15380         }
15381     },
15382
15383     /**
15384      * Normally the drag element is moved pixel by pixel, but we can specify
15385      * that it move a number of pixels at a time.  This method resolves the
15386      * location when we have it set up like this.
15387      * @method getTick
15388      * @param {int} val where we want to place the object
15389      * @param {int[]} tickArray sorted array of valid points
15390      * @return {int} the closest tick
15391      * @private
15392      */
15393     getTick: function(val, tickArray) {
15394
15395         if (!tickArray) {
15396             // If tick interval is not defined, it is effectively 1 pixel,
15397             // so we return the value passed to us.
15398             return val;
15399         } else if (tickArray[0] >= val) {
15400             // The value is lower than the first tick, so we return the first
15401             // tick.
15402             return tickArray[0];
15403         } else {
15404             for (var i=0, len=tickArray.length; i<len; ++i) {
15405                 var next = i + 1;
15406                 if (tickArray[next] && tickArray[next] >= val) {
15407                     var diff1 = val - tickArray[i];
15408                     var diff2 = tickArray[next] - val;
15409                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15410                 }
15411             }
15412
15413             // The value is larger than the last tick, so we return the last
15414             // tick.
15415             return tickArray[tickArray.length - 1];
15416         }
15417     },
15418
15419     /**
15420      * toString method
15421      * @method toString
15422      * @return {string} string representation of the dd obj
15423      */
15424     toString: function() {
15425         return ("DragDrop " + this.id);
15426     }
15427
15428 };
15429
15430 })();
15431 /*
15432  * Based on:
15433  * Ext JS Library 1.1.1
15434  * Copyright(c) 2006-2007, Ext JS, LLC.
15435  *
15436  * Originally Released Under LGPL - original licence link has changed is not relivant.
15437  *
15438  * Fork - LGPL
15439  * <script type="text/javascript">
15440  */
15441
15442
15443 /**
15444  * The drag and drop utility provides a framework for building drag and drop
15445  * applications.  In addition to enabling drag and drop for specific elements,
15446  * the drag and drop elements are tracked by the manager class, and the
15447  * interactions between the various elements are tracked during the drag and
15448  * the implementing code is notified about these important moments.
15449  */
15450
15451 // Only load the library once.  Rewriting the manager class would orphan
15452 // existing drag and drop instances.
15453 if (!Roo.dd.DragDropMgr) {
15454
15455 /**
15456  * @class Roo.dd.DragDropMgr
15457  * DragDropMgr is a singleton that tracks the element interaction for
15458  * all DragDrop items in the window.  Generally, you will not call
15459  * this class directly, but it does have helper methods that could
15460  * be useful in your DragDrop implementations.
15461  * @singleton
15462  */
15463 Roo.dd.DragDropMgr = function() {
15464
15465     var Event = Roo.EventManager;
15466
15467     return {
15468
15469         /**
15470          * Two dimensional Array of registered DragDrop objects.  The first
15471          * dimension is the DragDrop item group, the second the DragDrop
15472          * object.
15473          * @property ids
15474          * @type {string: string}
15475          * @private
15476          * @static
15477          */
15478         ids: {},
15479
15480         /**
15481          * Array of element ids defined as drag handles.  Used to determine
15482          * if the element that generated the mousedown event is actually the
15483          * handle and not the html element itself.
15484          * @property handleIds
15485          * @type {string: string}
15486          * @private
15487          * @static
15488          */
15489         handleIds: {},
15490
15491         /**
15492          * the DragDrop object that is currently being dragged
15493          * @property dragCurrent
15494          * @type DragDrop
15495          * @private
15496          * @static
15497          **/
15498         dragCurrent: null,
15499
15500         /**
15501          * the DragDrop object(s) that are being hovered over
15502          * @property dragOvers
15503          * @type Array
15504          * @private
15505          * @static
15506          */
15507         dragOvers: {},
15508
15509         /**
15510          * the X distance between the cursor and the object being dragged
15511          * @property deltaX
15512          * @type int
15513          * @private
15514          * @static
15515          */
15516         deltaX: 0,
15517
15518         /**
15519          * the Y distance between the cursor and the object being dragged
15520          * @property deltaY
15521          * @type int
15522          * @private
15523          * @static
15524          */
15525         deltaY: 0,
15526
15527         /**
15528          * Flag to determine if we should prevent the default behavior of the
15529          * events we define. By default this is true, but this can be set to
15530          * false if you need the default behavior (not recommended)
15531          * @property preventDefault
15532          * @type boolean
15533          * @static
15534          */
15535         preventDefault: true,
15536
15537         /**
15538          * Flag to determine if we should stop the propagation of the events
15539          * we generate. This is true by default but you may want to set it to
15540          * false if the html element contains other features that require the
15541          * mouse click.
15542          * @property stopPropagation
15543          * @type boolean
15544          * @static
15545          */
15546         stopPropagation: true,
15547
15548         /**
15549          * Internal flag that is set to true when drag and drop has been
15550          * intialized
15551          * @property initialized
15552          * @private
15553          * @static
15554          */
15555         initalized: false,
15556
15557         /**
15558          * All drag and drop can be disabled.
15559          * @property locked
15560          * @private
15561          * @static
15562          */
15563         locked: false,
15564
15565         /**
15566          * Called the first time an element is registered.
15567          * @method init
15568          * @private
15569          * @static
15570          */
15571         init: function() {
15572             this.initialized = true;
15573         },
15574
15575         /**
15576          * In point mode, drag and drop interaction is defined by the
15577          * location of the cursor during the drag/drop
15578          * @property POINT
15579          * @type int
15580          * @static
15581          */
15582         POINT: 0,
15583
15584         /**
15585          * In intersect mode, drag and drop interactio nis defined by the
15586          * overlap of two or more drag and drop objects.
15587          * @property INTERSECT
15588          * @type int
15589          * @static
15590          */
15591         INTERSECT: 1,
15592
15593         /**
15594          * The current drag and drop mode.  Default: POINT
15595          * @property mode
15596          * @type int
15597          * @static
15598          */
15599         mode: 0,
15600
15601         /**
15602          * Runs method on all drag and drop objects
15603          * @method _execOnAll
15604          * @private
15605          * @static
15606          */
15607         _execOnAll: function(sMethod, args) {
15608             for (var i in this.ids) {
15609                 for (var j in this.ids[i]) {
15610                     var oDD = this.ids[i][j];
15611                     if (! this.isTypeOfDD(oDD)) {
15612                         continue;
15613                     }
15614                     oDD[sMethod].apply(oDD, args);
15615                 }
15616             }
15617         },
15618
15619         /**
15620          * Drag and drop initialization.  Sets up the global event handlers
15621          * @method _onLoad
15622          * @private
15623          * @static
15624          */
15625         _onLoad: function() {
15626
15627             this.init();
15628
15629
15630             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15631             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15632             Event.on(window,   "unload",    this._onUnload, this, true);
15633             Event.on(window,   "resize",    this._onResize, this, true);
15634             // Event.on(window,   "mouseout",    this._test);
15635
15636         },
15637
15638         /**
15639          * Reset constraints on all drag and drop objs
15640          * @method _onResize
15641          * @private
15642          * @static
15643          */
15644         _onResize: function(e) {
15645             this._execOnAll("resetConstraints", []);
15646         },
15647
15648         /**
15649          * Lock all drag and drop functionality
15650          * @method lock
15651          * @static
15652          */
15653         lock: function() { this.locked = true; },
15654
15655         /**
15656          * Unlock all drag and drop functionality
15657          * @method unlock
15658          * @static
15659          */
15660         unlock: function() { this.locked = false; },
15661
15662         /**
15663          * Is drag and drop locked?
15664          * @method isLocked
15665          * @return {boolean} True if drag and drop is locked, false otherwise.
15666          * @static
15667          */
15668         isLocked: function() { return this.locked; },
15669
15670         /**
15671          * Location cache that is set for all drag drop objects when a drag is
15672          * initiated, cleared when the drag is finished.
15673          * @property locationCache
15674          * @private
15675          * @static
15676          */
15677         locationCache: {},
15678
15679         /**
15680          * Set useCache to false if you want to force object the lookup of each
15681          * drag and drop linked element constantly during a drag.
15682          * @property useCache
15683          * @type boolean
15684          * @static
15685          */
15686         useCache: true,
15687
15688         /**
15689          * The number of pixels that the mouse needs to move after the
15690          * mousedown before the drag is initiated.  Default=3;
15691          * @property clickPixelThresh
15692          * @type int
15693          * @static
15694          */
15695         clickPixelThresh: 3,
15696
15697         /**
15698          * The number of milliseconds after the mousedown event to initiate the
15699          * drag if we don't get a mouseup event. Default=1000
15700          * @property clickTimeThresh
15701          * @type int
15702          * @static
15703          */
15704         clickTimeThresh: 350,
15705
15706         /**
15707          * Flag that indicates that either the drag pixel threshold or the
15708          * mousdown time threshold has been met
15709          * @property dragThreshMet
15710          * @type boolean
15711          * @private
15712          * @static
15713          */
15714         dragThreshMet: false,
15715
15716         /**
15717          * Timeout used for the click time threshold
15718          * @property clickTimeout
15719          * @type Object
15720          * @private
15721          * @static
15722          */
15723         clickTimeout: null,
15724
15725         /**
15726          * The X position of the mousedown event stored for later use when a
15727          * drag threshold is met.
15728          * @property startX
15729          * @type int
15730          * @private
15731          * @static
15732          */
15733         startX: 0,
15734
15735         /**
15736          * The Y position of the mousedown event stored for later use when a
15737          * drag threshold is met.
15738          * @property startY
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         startY: 0,
15744
15745         /**
15746          * Each DragDrop instance must be registered with the DragDropMgr.
15747          * This is executed in DragDrop.init()
15748          * @method regDragDrop
15749          * @param {DragDrop} oDD the DragDrop object to register
15750          * @param {String} sGroup the name of the group this element belongs to
15751          * @static
15752          */
15753         regDragDrop: function(oDD, sGroup) {
15754             if (!this.initialized) { this.init(); }
15755
15756             if (!this.ids[sGroup]) {
15757                 this.ids[sGroup] = {};
15758             }
15759             this.ids[sGroup][oDD.id] = oDD;
15760         },
15761
15762         /**
15763          * Removes the supplied dd instance from the supplied group. Executed
15764          * by DragDrop.removeFromGroup, so don't call this function directly.
15765          * @method removeDDFromGroup
15766          * @private
15767          * @static
15768          */
15769         removeDDFromGroup: function(oDD, sGroup) {
15770             if (!this.ids[sGroup]) {
15771                 this.ids[sGroup] = {};
15772             }
15773
15774             var obj = this.ids[sGroup];
15775             if (obj && obj[oDD.id]) {
15776                 delete obj[oDD.id];
15777             }
15778         },
15779
15780         /**
15781          * Unregisters a drag and drop item.  This is executed in
15782          * DragDrop.unreg, use that method instead of calling this directly.
15783          * @method _remove
15784          * @private
15785          * @static
15786          */
15787         _remove: function(oDD) {
15788             for (var g in oDD.groups) {
15789                 if (g && this.ids[g][oDD.id]) {
15790                     delete this.ids[g][oDD.id];
15791                 }
15792             }
15793             delete this.handleIds[oDD.id];
15794         },
15795
15796         /**
15797          * Each DragDrop handle element must be registered.  This is done
15798          * automatically when executing DragDrop.setHandleElId()
15799          * @method regHandle
15800          * @param {String} sDDId the DragDrop id this element is a handle for
15801          * @param {String} sHandleId the id of the element that is the drag
15802          * handle
15803          * @static
15804          */
15805         regHandle: function(sDDId, sHandleId) {
15806             if (!this.handleIds[sDDId]) {
15807                 this.handleIds[sDDId] = {};
15808             }
15809             this.handleIds[sDDId][sHandleId] = sHandleId;
15810         },
15811
15812         /**
15813          * Utility function to determine if a given element has been
15814          * registered as a drag drop item.
15815          * @method isDragDrop
15816          * @param {String} id the element id to check
15817          * @return {boolean} true if this element is a DragDrop item,
15818          * false otherwise
15819          * @static
15820          */
15821         isDragDrop: function(id) {
15822             return ( this.getDDById(id) ) ? true : false;
15823         },
15824
15825         /**
15826          * Returns the drag and drop instances that are in all groups the
15827          * passed in instance belongs to.
15828          * @method getRelated
15829          * @param {DragDrop} p_oDD the obj to get related data for
15830          * @param {boolean} bTargetsOnly if true, only return targetable objs
15831          * @return {DragDrop[]} the related instances
15832          * @static
15833          */
15834         getRelated: function(p_oDD, bTargetsOnly) {
15835             var oDDs = [];
15836             for (var i in p_oDD.groups) {
15837                 for (j in this.ids[i]) {
15838                     var dd = this.ids[i][j];
15839                     if (! this.isTypeOfDD(dd)) {
15840                         continue;
15841                     }
15842                     if (!bTargetsOnly || dd.isTarget) {
15843                         oDDs[oDDs.length] = dd;
15844                     }
15845                 }
15846             }
15847
15848             return oDDs;
15849         },
15850
15851         /**
15852          * Returns true if the specified dd target is a legal target for
15853          * the specifice drag obj
15854          * @method isLegalTarget
15855          * @param {DragDrop} the drag obj
15856          * @param {DragDrop} the target
15857          * @return {boolean} true if the target is a legal target for the
15858          * dd obj
15859          * @static
15860          */
15861         isLegalTarget: function (oDD, oTargetDD) {
15862             var targets = this.getRelated(oDD, true);
15863             for (var i=0, len=targets.length;i<len;++i) {
15864                 if (targets[i].id == oTargetDD.id) {
15865                     return true;
15866                 }
15867             }
15868
15869             return false;
15870         },
15871
15872         /**
15873          * My goal is to be able to transparently determine if an object is
15874          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15875          * returns "object", oDD.constructor.toString() always returns
15876          * "DragDrop" and not the name of the subclass.  So for now it just
15877          * evaluates a well-known variable in DragDrop.
15878          * @method isTypeOfDD
15879          * @param {Object} the object to evaluate
15880          * @return {boolean} true if typeof oDD = DragDrop
15881          * @static
15882          */
15883         isTypeOfDD: function (oDD) {
15884             return (oDD && oDD.__ygDragDrop);
15885         },
15886
15887         /**
15888          * Utility function to determine if a given element has been
15889          * registered as a drag drop handle for the given Drag Drop object.
15890          * @method isHandle
15891          * @param {String} id the element id to check
15892          * @return {boolean} true if this element is a DragDrop handle, false
15893          * otherwise
15894          * @static
15895          */
15896         isHandle: function(sDDId, sHandleId) {
15897             return ( this.handleIds[sDDId] &&
15898                             this.handleIds[sDDId][sHandleId] );
15899         },
15900
15901         /**
15902          * Returns the DragDrop instance for a given id
15903          * @method getDDById
15904          * @param {String} id the id of the DragDrop object
15905          * @return {DragDrop} the drag drop object, null if it is not found
15906          * @static
15907          */
15908         getDDById: function(id) {
15909             for (var i in this.ids) {
15910                 if (this.ids[i][id]) {
15911                     return this.ids[i][id];
15912                 }
15913             }
15914             return null;
15915         },
15916
15917         /**
15918          * Fired after a registered DragDrop object gets the mousedown event.
15919          * Sets up the events required to track the object being dragged
15920          * @method handleMouseDown
15921          * @param {Event} e the event
15922          * @param oDD the DragDrop object being dragged
15923          * @private
15924          * @static
15925          */
15926         handleMouseDown: function(e, oDD) {
15927             if(Roo.QuickTips){
15928                 Roo.QuickTips.disable();
15929             }
15930             this.currentTarget = e.getTarget();
15931
15932             this.dragCurrent = oDD;
15933
15934             var el = oDD.getEl();
15935
15936             // track start position
15937             this.startX = e.getPageX();
15938             this.startY = e.getPageY();
15939
15940             this.deltaX = this.startX - el.offsetLeft;
15941             this.deltaY = this.startY - el.offsetTop;
15942
15943             this.dragThreshMet = false;
15944
15945             this.clickTimeout = setTimeout(
15946                     function() {
15947                         var DDM = Roo.dd.DDM;
15948                         DDM.startDrag(DDM.startX, DDM.startY);
15949                     },
15950                     this.clickTimeThresh );
15951         },
15952
15953         /**
15954          * Fired when either the drag pixel threshol or the mousedown hold
15955          * time threshold has been met.
15956          * @method startDrag
15957          * @param x {int} the X position of the original mousedown
15958          * @param y {int} the Y position of the original mousedown
15959          * @static
15960          */
15961         startDrag: function(x, y) {
15962             clearTimeout(this.clickTimeout);
15963             if (this.dragCurrent) {
15964                 this.dragCurrent.b4StartDrag(x, y);
15965                 this.dragCurrent.startDrag(x, y);
15966             }
15967             this.dragThreshMet = true;
15968         },
15969
15970         /**
15971          * Internal function to handle the mouseup event.  Will be invoked
15972          * from the context of the document.
15973          * @method handleMouseUp
15974          * @param {Event} e the event
15975          * @private
15976          * @static
15977          */
15978         handleMouseUp: function(e) {
15979
15980             if(Roo.QuickTips){
15981                 Roo.QuickTips.enable();
15982             }
15983             if (! this.dragCurrent) {
15984                 return;
15985             }
15986
15987             clearTimeout(this.clickTimeout);
15988
15989             if (this.dragThreshMet) {
15990                 this.fireEvents(e, true);
15991             } else {
15992             }
15993
15994             this.stopDrag(e);
15995
15996             this.stopEvent(e);
15997         },
15998
15999         /**
16000          * Utility to stop event propagation and event default, if these
16001          * features are turned on.
16002          * @method stopEvent
16003          * @param {Event} e the event as returned by this.getEvent()
16004          * @static
16005          */
16006         stopEvent: function(e){
16007             if(this.stopPropagation) {
16008                 e.stopPropagation();
16009             }
16010
16011             if (this.preventDefault) {
16012                 e.preventDefault();
16013             }
16014         },
16015
16016         /**
16017          * Internal function to clean up event handlers after the drag
16018          * operation is complete
16019          * @method stopDrag
16020          * @param {Event} e the event
16021          * @private
16022          * @static
16023          */
16024         stopDrag: function(e) {
16025             // Fire the drag end event for the item that was dragged
16026             if (this.dragCurrent) {
16027                 if (this.dragThreshMet) {
16028                     this.dragCurrent.b4EndDrag(e);
16029                     this.dragCurrent.endDrag(e);
16030                 }
16031
16032                 this.dragCurrent.onMouseUp(e);
16033             }
16034
16035             this.dragCurrent = null;
16036             this.dragOvers = {};
16037         },
16038
16039         /**
16040          * Internal function to handle the mousemove event.  Will be invoked
16041          * from the context of the html element.
16042          *
16043          * @TODO figure out what we can do about mouse events lost when the
16044          * user drags objects beyond the window boundary.  Currently we can
16045          * detect this in internet explorer by verifying that the mouse is
16046          * down during the mousemove event.  Firefox doesn't give us the
16047          * button state on the mousemove event.
16048          * @method handleMouseMove
16049          * @param {Event} e the event
16050          * @private
16051          * @static
16052          */
16053         handleMouseMove: function(e) {
16054             if (! this.dragCurrent) {
16055                 return true;
16056             }
16057
16058             // var button = e.which || e.button;
16059
16060             // check for IE mouseup outside of page boundary
16061             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16062                 this.stopEvent(e);
16063                 return this.handleMouseUp(e);
16064             }
16065
16066             if (!this.dragThreshMet) {
16067                 var diffX = Math.abs(this.startX - e.getPageX());
16068                 var diffY = Math.abs(this.startY - e.getPageY());
16069                 if (diffX > this.clickPixelThresh ||
16070                             diffY > this.clickPixelThresh) {
16071                     this.startDrag(this.startX, this.startY);
16072                 }
16073             }
16074
16075             if (this.dragThreshMet) {
16076                 this.dragCurrent.b4Drag(e);
16077                 this.dragCurrent.onDrag(e);
16078                 if(!this.dragCurrent.moveOnly){
16079                     this.fireEvents(e, false);
16080                 }
16081             }
16082
16083             this.stopEvent(e);
16084
16085             return true;
16086         },
16087
16088         /**
16089          * Iterates over all of the DragDrop elements to find ones we are
16090          * hovering over or dropping on
16091          * @method fireEvents
16092          * @param {Event} e the event
16093          * @param {boolean} isDrop is this a drop op or a mouseover op?
16094          * @private
16095          * @static
16096          */
16097         fireEvents: function(e, isDrop) {
16098             var dc = this.dragCurrent;
16099
16100             // If the user did the mouse up outside of the window, we could
16101             // get here even though we have ended the drag.
16102             if (!dc || dc.isLocked()) {
16103                 return;
16104             }
16105
16106             var pt = e.getPoint();
16107
16108             // cache the previous dragOver array
16109             var oldOvers = [];
16110
16111             var outEvts   = [];
16112             var overEvts  = [];
16113             var dropEvts  = [];
16114             var enterEvts = [];
16115
16116             // Check to see if the object(s) we were hovering over is no longer
16117             // being hovered over so we can fire the onDragOut event
16118             for (var i in this.dragOvers) {
16119
16120                 var ddo = this.dragOvers[i];
16121
16122                 if (! this.isTypeOfDD(ddo)) {
16123                     continue;
16124                 }
16125
16126                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16127                     outEvts.push( ddo );
16128                 }
16129
16130                 oldOvers[i] = true;
16131                 delete this.dragOvers[i];
16132             }
16133
16134             for (var sGroup in dc.groups) {
16135
16136                 if ("string" != typeof sGroup) {
16137                     continue;
16138                 }
16139
16140                 for (i in this.ids[sGroup]) {
16141                     var oDD = this.ids[sGroup][i];
16142                     if (! this.isTypeOfDD(oDD)) {
16143                         continue;
16144                     }
16145
16146                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16147                         if (this.isOverTarget(pt, oDD, this.mode)) {
16148                             // look for drop interactions
16149                             if (isDrop) {
16150                                 dropEvts.push( oDD );
16151                             // look for drag enter and drag over interactions
16152                             } else {
16153
16154                                 // initial drag over: dragEnter fires
16155                                 if (!oldOvers[oDD.id]) {
16156                                     enterEvts.push( oDD );
16157                                 // subsequent drag overs: dragOver fires
16158                                 } else {
16159                                     overEvts.push( oDD );
16160                                 }
16161
16162                                 this.dragOvers[oDD.id] = oDD;
16163                             }
16164                         }
16165                     }
16166                 }
16167             }
16168
16169             if (this.mode) {
16170                 if (outEvts.length) {
16171                     dc.b4DragOut(e, outEvts);
16172                     dc.onDragOut(e, outEvts);
16173                 }
16174
16175                 if (enterEvts.length) {
16176                     dc.onDragEnter(e, enterEvts);
16177                 }
16178
16179                 if (overEvts.length) {
16180                     dc.b4DragOver(e, overEvts);
16181                     dc.onDragOver(e, overEvts);
16182                 }
16183
16184                 if (dropEvts.length) {
16185                     dc.b4DragDrop(e, dropEvts);
16186                     dc.onDragDrop(e, dropEvts);
16187                 }
16188
16189             } else {
16190                 // fire dragout events
16191                 var len = 0;
16192                 for (i=0, len=outEvts.length; i<len; ++i) {
16193                     dc.b4DragOut(e, outEvts[i].id);
16194                     dc.onDragOut(e, outEvts[i].id);
16195                 }
16196
16197                 // fire enter events
16198                 for (i=0,len=enterEvts.length; i<len; ++i) {
16199                     // dc.b4DragEnter(e, oDD.id);
16200                     dc.onDragEnter(e, enterEvts[i].id);
16201                 }
16202
16203                 // fire over events
16204                 for (i=0,len=overEvts.length; i<len; ++i) {
16205                     dc.b4DragOver(e, overEvts[i].id);
16206                     dc.onDragOver(e, overEvts[i].id);
16207                 }
16208
16209                 // fire drop events
16210                 for (i=0, len=dropEvts.length; i<len; ++i) {
16211                     dc.b4DragDrop(e, dropEvts[i].id);
16212                     dc.onDragDrop(e, dropEvts[i].id);
16213                 }
16214
16215             }
16216
16217             // notify about a drop that did not find a target
16218             if (isDrop && !dropEvts.length) {
16219                 dc.onInvalidDrop(e);
16220             }
16221
16222         },
16223
16224         /**
16225          * Helper function for getting the best match from the list of drag
16226          * and drop objects returned by the drag and drop events when we are
16227          * in INTERSECT mode.  It returns either the first object that the
16228          * cursor is over, or the object that has the greatest overlap with
16229          * the dragged element.
16230          * @method getBestMatch
16231          * @param  {DragDrop[]} dds The array of drag and drop objects
16232          * targeted
16233          * @return {DragDrop}       The best single match
16234          * @static
16235          */
16236         getBestMatch: function(dds) {
16237             var winner = null;
16238             // Return null if the input is not what we expect
16239             //if (!dds || !dds.length || dds.length == 0) {
16240                // winner = null;
16241             // If there is only one item, it wins
16242             //} else if (dds.length == 1) {
16243
16244             var len = dds.length;
16245
16246             if (len == 1) {
16247                 winner = dds[0];
16248             } else {
16249                 // Loop through the targeted items
16250                 for (var i=0; i<len; ++i) {
16251                     var dd = dds[i];
16252                     // If the cursor is over the object, it wins.  If the
16253                     // cursor is over multiple matches, the first one we come
16254                     // to wins.
16255                     if (dd.cursorIsOver) {
16256                         winner = dd;
16257                         break;
16258                     // Otherwise the object with the most overlap wins
16259                     } else {
16260                         if (!winner ||
16261                             winner.overlap.getArea() < dd.overlap.getArea()) {
16262                             winner = dd;
16263                         }
16264                     }
16265                 }
16266             }
16267
16268             return winner;
16269         },
16270
16271         /**
16272          * Refreshes the cache of the top-left and bottom-right points of the
16273          * drag and drop objects in the specified group(s).  This is in the
16274          * format that is stored in the drag and drop instance, so typical
16275          * usage is:
16276          * <code>
16277          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16278          * </code>
16279          * Alternatively:
16280          * <code>
16281          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16282          * </code>
16283          * @TODO this really should be an indexed array.  Alternatively this
16284          * method could accept both.
16285          * @method refreshCache
16286          * @param {Object} groups an associative array of groups to refresh
16287          * @static
16288          */
16289         refreshCache: function(groups) {
16290             for (var sGroup in groups) {
16291                 if ("string" != typeof sGroup) {
16292                     continue;
16293                 }
16294                 for (var i in this.ids[sGroup]) {
16295                     var oDD = this.ids[sGroup][i];
16296
16297                     if (this.isTypeOfDD(oDD)) {
16298                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16299                         var loc = this.getLocation(oDD);
16300                         if (loc) {
16301                             this.locationCache[oDD.id] = loc;
16302                         } else {
16303                             delete this.locationCache[oDD.id];
16304                             // this will unregister the drag and drop object if
16305                             // the element is not in a usable state
16306                             // oDD.unreg();
16307                         }
16308                     }
16309                 }
16310             }
16311         },
16312
16313         /**
16314          * This checks to make sure an element exists and is in the DOM.  The
16315          * main purpose is to handle cases where innerHTML is used to remove
16316          * drag and drop objects from the DOM.  IE provides an 'unspecified
16317          * error' when trying to access the offsetParent of such an element
16318          * @method verifyEl
16319          * @param {HTMLElement} el the element to check
16320          * @return {boolean} true if the element looks usable
16321          * @static
16322          */
16323         verifyEl: function(el) {
16324             if (el) {
16325                 var parent;
16326                 if(Roo.isIE){
16327                     try{
16328                         parent = el.offsetParent;
16329                     }catch(e){}
16330                 }else{
16331                     parent = el.offsetParent;
16332                 }
16333                 if (parent) {
16334                     return true;
16335                 }
16336             }
16337
16338             return false;
16339         },
16340
16341         /**
16342          * Returns a Region object containing the drag and drop element's position
16343          * and size, including the padding configured for it
16344          * @method getLocation
16345          * @param {DragDrop} oDD the drag and drop object to get the
16346          *                       location for
16347          * @return {Roo.lib.Region} a Region object representing the total area
16348          *                             the element occupies, including any padding
16349          *                             the instance is configured for.
16350          * @static
16351          */
16352         getLocation: function(oDD) {
16353             if (! this.isTypeOfDD(oDD)) {
16354                 return null;
16355             }
16356
16357             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16358
16359             try {
16360                 pos= Roo.lib.Dom.getXY(el);
16361             } catch (e) { }
16362
16363             if (!pos) {
16364                 return null;
16365             }
16366
16367             x1 = pos[0];
16368             x2 = x1 + el.offsetWidth;
16369             y1 = pos[1];
16370             y2 = y1 + el.offsetHeight;
16371
16372             t = y1 - oDD.padding[0];
16373             r = x2 + oDD.padding[1];
16374             b = y2 + oDD.padding[2];
16375             l = x1 - oDD.padding[3];
16376
16377             return new Roo.lib.Region( t, r, b, l );
16378         },
16379
16380         /**
16381          * Checks the cursor location to see if it over the target
16382          * @method isOverTarget
16383          * @param {Roo.lib.Point} pt The point to evaluate
16384          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16385          * @return {boolean} true if the mouse is over the target
16386          * @private
16387          * @static
16388          */
16389         isOverTarget: function(pt, oTarget, intersect) {
16390             // use cache if available
16391             var loc = this.locationCache[oTarget.id];
16392             if (!loc || !this.useCache) {
16393                 loc = this.getLocation(oTarget);
16394                 this.locationCache[oTarget.id] = loc;
16395
16396             }
16397
16398             if (!loc) {
16399                 return false;
16400             }
16401
16402             oTarget.cursorIsOver = loc.contains( pt );
16403
16404             // DragDrop is using this as a sanity check for the initial mousedown
16405             // in this case we are done.  In POINT mode, if the drag obj has no
16406             // contraints, we are also done. Otherwise we need to evaluate the
16407             // location of the target as related to the actual location of the
16408             // dragged element.
16409             var dc = this.dragCurrent;
16410             if (!dc || !dc.getTargetCoord ||
16411                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16412                 return oTarget.cursorIsOver;
16413             }
16414
16415             oTarget.overlap = null;
16416
16417             // Get the current location of the drag element, this is the
16418             // location of the mouse event less the delta that represents
16419             // where the original mousedown happened on the element.  We
16420             // need to consider constraints and ticks as well.
16421             var pos = dc.getTargetCoord(pt.x, pt.y);
16422
16423             var el = dc.getDragEl();
16424             var curRegion = new Roo.lib.Region( pos.y,
16425                                                    pos.x + el.offsetWidth,
16426                                                    pos.y + el.offsetHeight,
16427                                                    pos.x );
16428
16429             var overlap = curRegion.intersect(loc);
16430
16431             if (overlap) {
16432                 oTarget.overlap = overlap;
16433                 return (intersect) ? true : oTarget.cursorIsOver;
16434             } else {
16435                 return false;
16436             }
16437         },
16438
16439         /**
16440          * unload event handler
16441          * @method _onUnload
16442          * @private
16443          * @static
16444          */
16445         _onUnload: function(e, me) {
16446             Roo.dd.DragDropMgr.unregAll();
16447         },
16448
16449         /**
16450          * Cleans up the drag and drop events and objects.
16451          * @method unregAll
16452          * @private
16453          * @static
16454          */
16455         unregAll: function() {
16456
16457             if (this.dragCurrent) {
16458                 this.stopDrag();
16459                 this.dragCurrent = null;
16460             }
16461
16462             this._execOnAll("unreg", []);
16463
16464             for (i in this.elementCache) {
16465                 delete this.elementCache[i];
16466             }
16467
16468             this.elementCache = {};
16469             this.ids = {};
16470         },
16471
16472         /**
16473          * A cache of DOM elements
16474          * @property elementCache
16475          * @private
16476          * @static
16477          */
16478         elementCache: {},
16479
16480         /**
16481          * Get the wrapper for the DOM element specified
16482          * @method getElWrapper
16483          * @param {String} id the id of the element to get
16484          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16485          * @private
16486          * @deprecated This wrapper isn't that useful
16487          * @static
16488          */
16489         getElWrapper: function(id) {
16490             var oWrapper = this.elementCache[id];
16491             if (!oWrapper || !oWrapper.el) {
16492                 oWrapper = this.elementCache[id] =
16493                     new this.ElementWrapper(Roo.getDom(id));
16494             }
16495             return oWrapper;
16496         },
16497
16498         /**
16499          * Returns the actual DOM element
16500          * @method getElement
16501          * @param {String} id the id of the elment to get
16502          * @return {Object} The element
16503          * @deprecated use Roo.getDom instead
16504          * @static
16505          */
16506         getElement: function(id) {
16507             return Roo.getDom(id);
16508         },
16509
16510         /**
16511          * Returns the style property for the DOM element (i.e.,
16512          * document.getElById(id).style)
16513          * @method getCss
16514          * @param {String} id the id of the elment to get
16515          * @return {Object} The style property of the element
16516          * @deprecated use Roo.getDom instead
16517          * @static
16518          */
16519         getCss: function(id) {
16520             var el = Roo.getDom(id);
16521             return (el) ? el.style : null;
16522         },
16523
16524         /**
16525          * Inner class for cached elements
16526          * @class DragDropMgr.ElementWrapper
16527          * @for DragDropMgr
16528          * @private
16529          * @deprecated
16530          */
16531         ElementWrapper: function(el) {
16532                 /**
16533                  * The element
16534                  * @property el
16535                  */
16536                 this.el = el || null;
16537                 /**
16538                  * The element id
16539                  * @property id
16540                  */
16541                 this.id = this.el && el.id;
16542                 /**
16543                  * A reference to the style property
16544                  * @property css
16545                  */
16546                 this.css = this.el && el.style;
16547             },
16548
16549         /**
16550          * Returns the X position of an html element
16551          * @method getPosX
16552          * @param el the element for which to get the position
16553          * @return {int} the X coordinate
16554          * @for DragDropMgr
16555          * @deprecated use Roo.lib.Dom.getX instead
16556          * @static
16557          */
16558         getPosX: function(el) {
16559             return Roo.lib.Dom.getX(el);
16560         },
16561
16562         /**
16563          * Returns the Y position of an html element
16564          * @method getPosY
16565          * @param el the element for which to get the position
16566          * @return {int} the Y coordinate
16567          * @deprecated use Roo.lib.Dom.getY instead
16568          * @static
16569          */
16570         getPosY: function(el) {
16571             return Roo.lib.Dom.getY(el);
16572         },
16573
16574         /**
16575          * Swap two nodes.  In IE, we use the native method, for others we
16576          * emulate the IE behavior
16577          * @method swapNode
16578          * @param n1 the first node to swap
16579          * @param n2 the other node to swap
16580          * @static
16581          */
16582         swapNode: function(n1, n2) {
16583             if (n1.swapNode) {
16584                 n1.swapNode(n2);
16585             } else {
16586                 var p = n2.parentNode;
16587                 var s = n2.nextSibling;
16588
16589                 if (s == n1) {
16590                     p.insertBefore(n1, n2);
16591                 } else if (n2 == n1.nextSibling) {
16592                     p.insertBefore(n2, n1);
16593                 } else {
16594                     n1.parentNode.replaceChild(n2, n1);
16595                     p.insertBefore(n1, s);
16596                 }
16597             }
16598         },
16599
16600         /**
16601          * Returns the current scroll position
16602          * @method getScroll
16603          * @private
16604          * @static
16605          */
16606         getScroll: function () {
16607             var t, l, dde=document.documentElement, db=document.body;
16608             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16609                 t = dde.scrollTop;
16610                 l = dde.scrollLeft;
16611             } else if (db) {
16612                 t = db.scrollTop;
16613                 l = db.scrollLeft;
16614             } else {
16615
16616             }
16617             return { top: t, left: l };
16618         },
16619
16620         /**
16621          * Returns the specified element style property
16622          * @method getStyle
16623          * @param {HTMLElement} el          the element
16624          * @param {string}      styleProp   the style property
16625          * @return {string} The value of the style property
16626          * @deprecated use Roo.lib.Dom.getStyle
16627          * @static
16628          */
16629         getStyle: function(el, styleProp) {
16630             return Roo.fly(el).getStyle(styleProp);
16631         },
16632
16633         /**
16634          * Gets the scrollTop
16635          * @method getScrollTop
16636          * @return {int} the document's scrollTop
16637          * @static
16638          */
16639         getScrollTop: function () { return this.getScroll().top; },
16640
16641         /**
16642          * Gets the scrollLeft
16643          * @method getScrollLeft
16644          * @return {int} the document's scrollTop
16645          * @static
16646          */
16647         getScrollLeft: function () { return this.getScroll().left; },
16648
16649         /**
16650          * Sets the x/y position of an element to the location of the
16651          * target element.
16652          * @method moveToEl
16653          * @param {HTMLElement} moveEl      The element to move
16654          * @param {HTMLElement} targetEl    The position reference element
16655          * @static
16656          */
16657         moveToEl: function (moveEl, targetEl) {
16658             var aCoord = Roo.lib.Dom.getXY(targetEl);
16659             Roo.lib.Dom.setXY(moveEl, aCoord);
16660         },
16661
16662         /**
16663          * Numeric array sort function
16664          * @method numericSort
16665          * @static
16666          */
16667         numericSort: function(a, b) { return (a - b); },
16668
16669         /**
16670          * Internal counter
16671          * @property _timeoutCount
16672          * @private
16673          * @static
16674          */
16675         _timeoutCount: 0,
16676
16677         /**
16678          * Trying to make the load order less important.  Without this we get
16679          * an error if this file is loaded before the Event Utility.
16680          * @method _addListeners
16681          * @private
16682          * @static
16683          */
16684         _addListeners: function() {
16685             var DDM = Roo.dd.DDM;
16686             if ( Roo.lib.Event && document ) {
16687                 DDM._onLoad();
16688             } else {
16689                 if (DDM._timeoutCount > 2000) {
16690                 } else {
16691                     setTimeout(DDM._addListeners, 10);
16692                     if (document && document.body) {
16693                         DDM._timeoutCount += 1;
16694                     }
16695                 }
16696             }
16697         },
16698
16699         /**
16700          * Recursively searches the immediate parent and all child nodes for
16701          * the handle element in order to determine wheter or not it was
16702          * clicked.
16703          * @method handleWasClicked
16704          * @param node the html element to inspect
16705          * @static
16706          */
16707         handleWasClicked: function(node, id) {
16708             if (this.isHandle(id, node.id)) {
16709                 return true;
16710             } else {
16711                 // check to see if this is a text node child of the one we want
16712                 var p = node.parentNode;
16713
16714                 while (p) {
16715                     if (this.isHandle(id, p.id)) {
16716                         return true;
16717                     } else {
16718                         p = p.parentNode;
16719                     }
16720                 }
16721             }
16722
16723             return false;
16724         }
16725
16726     };
16727
16728 }();
16729
16730 // shorter alias, save a few bytes
16731 Roo.dd.DDM = Roo.dd.DragDropMgr;
16732 Roo.dd.DDM._addListeners();
16733
16734 }/*
16735  * Based on:
16736  * Ext JS Library 1.1.1
16737  * Copyright(c) 2006-2007, Ext JS, LLC.
16738  *
16739  * Originally Released Under LGPL - original licence link has changed is not relivant.
16740  *
16741  * Fork - LGPL
16742  * <script type="text/javascript">
16743  */
16744
16745 /**
16746  * @class Roo.dd.DD
16747  * A DragDrop implementation where the linked element follows the
16748  * mouse cursor during a drag.
16749  * @extends Roo.dd.DragDrop
16750  * @constructor
16751  * @param {String} id the id of the linked element
16752  * @param {String} sGroup the group of related DragDrop items
16753  * @param {object} config an object containing configurable attributes
16754  *                Valid properties for DD:
16755  *                    scroll
16756  */
16757 Roo.dd.DD = function(id, sGroup, config) {
16758     if (id) {
16759         this.init(id, sGroup, config);
16760     }
16761 };
16762
16763 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16764
16765     /**
16766      * When set to true, the utility automatically tries to scroll the browser
16767      * window wehn a drag and drop element is dragged near the viewport boundary.
16768      * Defaults to true.
16769      * @property scroll
16770      * @type boolean
16771      */
16772     scroll: true,
16773
16774     /**
16775      * Sets the pointer offset to the distance between the linked element's top
16776      * left corner and the location the element was clicked
16777      * @method autoOffset
16778      * @param {int} iPageX the X coordinate of the click
16779      * @param {int} iPageY the Y coordinate of the click
16780      */
16781     autoOffset: function(iPageX, iPageY) {
16782         var x = iPageX - this.startPageX;
16783         var y = iPageY - this.startPageY;
16784         this.setDelta(x, y);
16785     },
16786
16787     /**
16788      * Sets the pointer offset.  You can call this directly to force the
16789      * offset to be in a particular location (e.g., pass in 0,0 to set it
16790      * to the center of the object)
16791      * @method setDelta
16792      * @param {int} iDeltaX the distance from the left
16793      * @param {int} iDeltaY the distance from the top
16794      */
16795     setDelta: function(iDeltaX, iDeltaY) {
16796         this.deltaX = iDeltaX;
16797         this.deltaY = iDeltaY;
16798     },
16799
16800     /**
16801      * Sets the drag element to the location of the mousedown or click event,
16802      * maintaining the cursor location relative to the location on the element
16803      * that was clicked.  Override this if you want to place the element in a
16804      * location other than where the cursor is.
16805      * @method setDragElPos
16806      * @param {int} iPageX the X coordinate of the mousedown or drag event
16807      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16808      */
16809     setDragElPos: function(iPageX, iPageY) {
16810         // the first time we do this, we are going to check to make sure
16811         // the element has css positioning
16812
16813         var el = this.getDragEl();
16814         this.alignElWithMouse(el, iPageX, iPageY);
16815     },
16816
16817     /**
16818      * Sets the element to the location of the mousedown or click event,
16819      * maintaining the cursor location relative to the location on the element
16820      * that was clicked.  Override this if you want to place the element in a
16821      * location other than where the cursor is.
16822      * @method alignElWithMouse
16823      * @param {HTMLElement} el the element to move
16824      * @param {int} iPageX the X coordinate of the mousedown or drag event
16825      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16826      */
16827     alignElWithMouse: function(el, iPageX, iPageY) {
16828         var oCoord = this.getTargetCoord(iPageX, iPageY);
16829         var fly = el.dom ? el : Roo.fly(el);
16830         if (!this.deltaSetXY) {
16831             var aCoord = [oCoord.x, oCoord.y];
16832             fly.setXY(aCoord);
16833             var newLeft = fly.getLeft(true);
16834             var newTop  = fly.getTop(true);
16835             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16836         } else {
16837             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16838         }
16839
16840         this.cachePosition(oCoord.x, oCoord.y);
16841         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16842         return oCoord;
16843     },
16844
16845     /**
16846      * Saves the most recent position so that we can reset the constraints and
16847      * tick marks on-demand.  We need to know this so that we can calculate the
16848      * number of pixels the element is offset from its original position.
16849      * @method cachePosition
16850      * @param iPageX the current x position (optional, this just makes it so we
16851      * don't have to look it up again)
16852      * @param iPageY the current y position (optional, this just makes it so we
16853      * don't have to look it up again)
16854      */
16855     cachePosition: function(iPageX, iPageY) {
16856         if (iPageX) {
16857             this.lastPageX = iPageX;
16858             this.lastPageY = iPageY;
16859         } else {
16860             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16861             this.lastPageX = aCoord[0];
16862             this.lastPageY = aCoord[1];
16863         }
16864     },
16865
16866     /**
16867      * Auto-scroll the window if the dragged object has been moved beyond the
16868      * visible window boundary.
16869      * @method autoScroll
16870      * @param {int} x the drag element's x position
16871      * @param {int} y the drag element's y position
16872      * @param {int} h the height of the drag element
16873      * @param {int} w the width of the drag element
16874      * @private
16875      */
16876     autoScroll: function(x, y, h, w) {
16877
16878         if (this.scroll) {
16879             // The client height
16880             var clientH = Roo.lib.Dom.getViewWidth();
16881
16882             // The client width
16883             var clientW = Roo.lib.Dom.getViewHeight();
16884
16885             // The amt scrolled down
16886             var st = this.DDM.getScrollTop();
16887
16888             // The amt scrolled right
16889             var sl = this.DDM.getScrollLeft();
16890
16891             // Location of the bottom of the element
16892             var bot = h + y;
16893
16894             // Location of the right of the element
16895             var right = w + x;
16896
16897             // The distance from the cursor to the bottom of the visible area,
16898             // adjusted so that we don't scroll if the cursor is beyond the
16899             // element drag constraints
16900             var toBot = (clientH + st - y - this.deltaY);
16901
16902             // The distance from the cursor to the right of the visible area
16903             var toRight = (clientW + sl - x - this.deltaX);
16904
16905
16906             // How close to the edge the cursor must be before we scroll
16907             // var thresh = (document.all) ? 100 : 40;
16908             var thresh = 40;
16909
16910             // How many pixels to scroll per autoscroll op.  This helps to reduce
16911             // clunky scrolling. IE is more sensitive about this ... it needs this
16912             // value to be higher.
16913             var scrAmt = (document.all) ? 80 : 30;
16914
16915             // Scroll down if we are near the bottom of the visible page and the
16916             // obj extends below the crease
16917             if ( bot > clientH && toBot < thresh ) {
16918                 window.scrollTo(sl, st + scrAmt);
16919             }
16920
16921             // Scroll up if the window is scrolled down and the top of the object
16922             // goes above the top border
16923             if ( y < st && st > 0 && y - st < thresh ) {
16924                 window.scrollTo(sl, st - scrAmt);
16925             }
16926
16927             // Scroll right if the obj is beyond the right border and the cursor is
16928             // near the border.
16929             if ( right > clientW && toRight < thresh ) {
16930                 window.scrollTo(sl + scrAmt, st);
16931             }
16932
16933             // Scroll left if the window has been scrolled to the right and the obj
16934             // extends past the left border
16935             if ( x < sl && sl > 0 && x - sl < thresh ) {
16936                 window.scrollTo(sl - scrAmt, st);
16937             }
16938         }
16939     },
16940
16941     /**
16942      * Finds the location the element should be placed if we want to move
16943      * it to where the mouse location less the click offset would place us.
16944      * @method getTargetCoord
16945      * @param {int} iPageX the X coordinate of the click
16946      * @param {int} iPageY the Y coordinate of the click
16947      * @return an object that contains the coordinates (Object.x and Object.y)
16948      * @private
16949      */
16950     getTargetCoord: function(iPageX, iPageY) {
16951
16952
16953         var x = iPageX - this.deltaX;
16954         var y = iPageY - this.deltaY;
16955
16956         if (this.constrainX) {
16957             if (x < this.minX) { x = this.minX; }
16958             if (x > this.maxX) { x = this.maxX; }
16959         }
16960
16961         if (this.constrainY) {
16962             if (y < this.minY) { y = this.minY; }
16963             if (y > this.maxY) { y = this.maxY; }
16964         }
16965
16966         x = this.getTick(x, this.xTicks);
16967         y = this.getTick(y, this.yTicks);
16968
16969
16970         return {x:x, y:y};
16971     },
16972
16973     /*
16974      * Sets up config options specific to this class. Overrides
16975      * Roo.dd.DragDrop, but all versions of this method through the
16976      * inheritance chain are called
16977      */
16978     applyConfig: function() {
16979         Roo.dd.DD.superclass.applyConfig.call(this);
16980         this.scroll = (this.config.scroll !== false);
16981     },
16982
16983     /*
16984      * Event that fires prior to the onMouseDown event.  Overrides
16985      * Roo.dd.DragDrop.
16986      */
16987     b4MouseDown: function(e) {
16988         // this.resetConstraints();
16989         this.autoOffset(e.getPageX(),
16990                             e.getPageY());
16991     },
16992
16993     /*
16994      * Event that fires prior to the onDrag event.  Overrides
16995      * Roo.dd.DragDrop.
16996      */
16997     b4Drag: function(e) {
16998         this.setDragElPos(e.getPageX(),
16999                             e.getPageY());
17000     },
17001
17002     toString: function() {
17003         return ("DD " + this.id);
17004     }
17005
17006     //////////////////////////////////////////////////////////////////////////
17007     // Debugging ygDragDrop events that can be overridden
17008     //////////////////////////////////////////////////////////////////////////
17009     /*
17010     startDrag: function(x, y) {
17011     },
17012
17013     onDrag: function(e) {
17014     },
17015
17016     onDragEnter: function(e, id) {
17017     },
17018
17019     onDragOver: function(e, id) {
17020     },
17021
17022     onDragOut: function(e, id) {
17023     },
17024
17025     onDragDrop: function(e, id) {
17026     },
17027
17028     endDrag: function(e) {
17029     }
17030
17031     */
17032
17033 });/*
17034  * Based on:
17035  * Ext JS Library 1.1.1
17036  * Copyright(c) 2006-2007, Ext JS, LLC.
17037  *
17038  * Originally Released Under LGPL - original licence link has changed is not relivant.
17039  *
17040  * Fork - LGPL
17041  * <script type="text/javascript">
17042  */
17043
17044 /**
17045  * @class Roo.dd.DDProxy
17046  * A DragDrop implementation that inserts an empty, bordered div into
17047  * the document that follows the cursor during drag operations.  At the time of
17048  * the click, the frame div is resized to the dimensions of the linked html
17049  * element, and moved to the exact location of the linked element.
17050  *
17051  * References to the "frame" element refer to the single proxy element that
17052  * was created to be dragged in place of all DDProxy elements on the
17053  * page.
17054  *
17055  * @extends Roo.dd.DD
17056  * @constructor
17057  * @param {String} id the id of the linked html element
17058  * @param {String} sGroup the group of related DragDrop objects
17059  * @param {object} config an object containing configurable attributes
17060  *                Valid properties for DDProxy in addition to those in DragDrop:
17061  *                   resizeFrame, centerFrame, dragElId
17062  */
17063 Roo.dd.DDProxy = function(id, sGroup, config) {
17064     if (id) {
17065         this.init(id, sGroup, config);
17066         this.initFrame();
17067     }
17068 };
17069
17070 /**
17071  * The default drag frame div id
17072  * @property Roo.dd.DDProxy.dragElId
17073  * @type String
17074  * @static
17075  */
17076 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17077
17078 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17079
17080     /**
17081      * By default we resize the drag frame to be the same size as the element
17082      * we want to drag (this is to get the frame effect).  We can turn it off
17083      * if we want a different behavior.
17084      * @property resizeFrame
17085      * @type boolean
17086      */
17087     resizeFrame: true,
17088
17089     /**
17090      * By default the frame is positioned exactly where the drag element is, so
17091      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17092      * you do not have constraints on the obj is to have the drag frame centered
17093      * around the cursor.  Set centerFrame to true for this effect.
17094      * @property centerFrame
17095      * @type boolean
17096      */
17097     centerFrame: false,
17098
17099     /**
17100      * Creates the proxy element if it does not yet exist
17101      * @method createFrame
17102      */
17103     createFrame: function() {
17104         var self = this;
17105         var body = document.body;
17106
17107         if (!body || !body.firstChild) {
17108             setTimeout( function() { self.createFrame(); }, 50 );
17109             return;
17110         }
17111
17112         var div = this.getDragEl();
17113
17114         if (!div) {
17115             div    = document.createElement("div");
17116             div.id = this.dragElId;
17117             var s  = div.style;
17118
17119             s.position   = "absolute";
17120             s.visibility = "hidden";
17121             s.cursor     = "move";
17122             s.border     = "2px solid #aaa";
17123             s.zIndex     = 999;
17124
17125             // appendChild can blow up IE if invoked prior to the window load event
17126             // while rendering a table.  It is possible there are other scenarios
17127             // that would cause this to happen as well.
17128             body.insertBefore(div, body.firstChild);
17129         }
17130     },
17131
17132     /**
17133      * Initialization for the drag frame element.  Must be called in the
17134      * constructor of all subclasses
17135      * @method initFrame
17136      */
17137     initFrame: function() {
17138         this.createFrame();
17139     },
17140
17141     applyConfig: function() {
17142         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17143
17144         this.resizeFrame = (this.config.resizeFrame !== false);
17145         this.centerFrame = (this.config.centerFrame);
17146         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17147     },
17148
17149     /**
17150      * Resizes the drag frame to the dimensions of the clicked object, positions
17151      * it over the object, and finally displays it
17152      * @method showFrame
17153      * @param {int} iPageX X click position
17154      * @param {int} iPageY Y click position
17155      * @private
17156      */
17157     showFrame: function(iPageX, iPageY) {
17158         var el = this.getEl();
17159         var dragEl = this.getDragEl();
17160         var s = dragEl.style;
17161
17162         this._resizeProxy();
17163
17164         if (this.centerFrame) {
17165             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17166                            Math.round(parseInt(s.height, 10)/2) );
17167         }
17168
17169         this.setDragElPos(iPageX, iPageY);
17170
17171         Roo.fly(dragEl).show();
17172     },
17173
17174     /**
17175      * The proxy is automatically resized to the dimensions of the linked
17176      * element when a drag is initiated, unless resizeFrame is set to false
17177      * @method _resizeProxy
17178      * @private
17179      */
17180     _resizeProxy: function() {
17181         if (this.resizeFrame) {
17182             var el = this.getEl();
17183             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17184         }
17185     },
17186
17187     // overrides Roo.dd.DragDrop
17188     b4MouseDown: function(e) {
17189         var x = e.getPageX();
17190         var y = e.getPageY();
17191         this.autoOffset(x, y);
17192         this.setDragElPos(x, y);
17193     },
17194
17195     // overrides Roo.dd.DragDrop
17196     b4StartDrag: function(x, y) {
17197         // show the drag frame
17198         this.showFrame(x, y);
17199     },
17200
17201     // overrides Roo.dd.DragDrop
17202     b4EndDrag: function(e) {
17203         Roo.fly(this.getDragEl()).hide();
17204     },
17205
17206     // overrides Roo.dd.DragDrop
17207     // By default we try to move the element to the last location of the frame.
17208     // This is so that the default behavior mirrors that of Roo.dd.DD.
17209     endDrag: function(e) {
17210
17211         var lel = this.getEl();
17212         var del = this.getDragEl();
17213
17214         // Show the drag frame briefly so we can get its position
17215         del.style.visibility = "";
17216
17217         this.beforeMove();
17218         // Hide the linked element before the move to get around a Safari
17219         // rendering bug.
17220         lel.style.visibility = "hidden";
17221         Roo.dd.DDM.moveToEl(lel, del);
17222         del.style.visibility = "hidden";
17223         lel.style.visibility = "";
17224
17225         this.afterDrag();
17226     },
17227
17228     beforeMove : function(){
17229
17230     },
17231
17232     afterDrag : function(){
17233
17234     },
17235
17236     toString: function() {
17237         return ("DDProxy " + this.id);
17238     }
17239
17240 });
17241 /*
17242  * Based on:
17243  * Ext JS Library 1.1.1
17244  * Copyright(c) 2006-2007, Ext JS, LLC.
17245  *
17246  * Originally Released Under LGPL - original licence link has changed is not relivant.
17247  *
17248  * Fork - LGPL
17249  * <script type="text/javascript">
17250  */
17251
17252  /**
17253  * @class Roo.dd.DDTarget
17254  * A DragDrop implementation that does not move, but can be a drop
17255  * target.  You would get the same result by simply omitting implementation
17256  * for the event callbacks, but this way we reduce the processing cost of the
17257  * event listener and the callbacks.
17258  * @extends Roo.dd.DragDrop
17259  * @constructor
17260  * @param {String} id the id of the element that is a drop target
17261  * @param {String} sGroup the group of related DragDrop objects
17262  * @param {object} config an object containing configurable attributes
17263  *                 Valid properties for DDTarget in addition to those in
17264  *                 DragDrop:
17265  *                    none
17266  */
17267 Roo.dd.DDTarget = function(id, sGroup, config) {
17268     if (id) {
17269         this.initTarget(id, sGroup, config);
17270     }
17271 };
17272
17273 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17274 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17275     toString: function() {
17276         return ("DDTarget " + this.id);
17277     }
17278 });
17279 /*
17280  * Based on:
17281  * Ext JS Library 1.1.1
17282  * Copyright(c) 2006-2007, Ext JS, LLC.
17283  *
17284  * Originally Released Under LGPL - original licence link has changed is not relivant.
17285  *
17286  * Fork - LGPL
17287  * <script type="text/javascript">
17288  */
17289  
17290
17291 /**
17292  * @class Roo.dd.ScrollManager
17293  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17294  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17295  * @singleton
17296  */
17297 Roo.dd.ScrollManager = function(){
17298     var ddm = Roo.dd.DragDropMgr;
17299     var els = {};
17300     var dragEl = null;
17301     var proc = {};
17302     
17303     var onStop = function(e){
17304         dragEl = null;
17305         clearProc();
17306     };
17307     
17308     var triggerRefresh = function(){
17309         if(ddm.dragCurrent){
17310              ddm.refreshCache(ddm.dragCurrent.groups);
17311         }
17312     };
17313     
17314     var doScroll = function(){
17315         if(ddm.dragCurrent){
17316             var dds = Roo.dd.ScrollManager;
17317             if(!dds.animate){
17318                 if(proc.el.scroll(proc.dir, dds.increment)){
17319                     triggerRefresh();
17320                 }
17321             }else{
17322                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17323             }
17324         }
17325     };
17326     
17327     var clearProc = function(){
17328         if(proc.id){
17329             clearInterval(proc.id);
17330         }
17331         proc.id = 0;
17332         proc.el = null;
17333         proc.dir = "";
17334     };
17335     
17336     var startProc = function(el, dir){
17337         clearProc();
17338         proc.el = el;
17339         proc.dir = dir;
17340         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17341     };
17342     
17343     var onFire = function(e, isDrop){
17344         if(isDrop || !ddm.dragCurrent){ return; }
17345         var dds = Roo.dd.ScrollManager;
17346         if(!dragEl || dragEl != ddm.dragCurrent){
17347             dragEl = ddm.dragCurrent;
17348             // refresh regions on drag start
17349             dds.refreshCache();
17350         }
17351         
17352         var xy = Roo.lib.Event.getXY(e);
17353         var pt = new Roo.lib.Point(xy[0], xy[1]);
17354         for(var id in els){
17355             var el = els[id], r = el._region;
17356             if(r && r.contains(pt) && el.isScrollable()){
17357                 if(r.bottom - pt.y <= dds.thresh){
17358                     if(proc.el != el){
17359                         startProc(el, "down");
17360                     }
17361                     return;
17362                 }else if(r.right - pt.x <= dds.thresh){
17363                     if(proc.el != el){
17364                         startProc(el, "left");
17365                     }
17366                     return;
17367                 }else if(pt.y - r.top <= dds.thresh){
17368                     if(proc.el != el){
17369                         startProc(el, "up");
17370                     }
17371                     return;
17372                 }else if(pt.x - r.left <= dds.thresh){
17373                     if(proc.el != el){
17374                         startProc(el, "right");
17375                     }
17376                     return;
17377                 }
17378             }
17379         }
17380         clearProc();
17381     };
17382     
17383     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17384     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17385     
17386     return {
17387         /**
17388          * Registers new overflow element(s) to auto scroll
17389          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17390          */
17391         register : function(el){
17392             if(el instanceof Array){
17393                 for(var i = 0, len = el.length; i < len; i++) {
17394                         this.register(el[i]);
17395                 }
17396             }else{
17397                 el = Roo.get(el);
17398                 els[el.id] = el;
17399             }
17400         },
17401         
17402         /**
17403          * Unregisters overflow element(s) so they are no longer scrolled
17404          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17405          */
17406         unregister : function(el){
17407             if(el instanceof Array){
17408                 for(var i = 0, len = el.length; i < len; i++) {
17409                         this.unregister(el[i]);
17410                 }
17411             }else{
17412                 el = Roo.get(el);
17413                 delete els[el.id];
17414             }
17415         },
17416         
17417         /**
17418          * The number of pixels from the edge of a container the pointer needs to be to 
17419          * trigger scrolling (defaults to 25)
17420          * @type Number
17421          */
17422         thresh : 25,
17423         
17424         /**
17425          * The number of pixels to scroll in each scroll increment (defaults to 50)
17426          * @type Number
17427          */
17428         increment : 100,
17429         
17430         /**
17431          * The frequency of scrolls in milliseconds (defaults to 500)
17432          * @type Number
17433          */
17434         frequency : 500,
17435         
17436         /**
17437          * True to animate the scroll (defaults to true)
17438          * @type Boolean
17439          */
17440         animate: true,
17441         
17442         /**
17443          * The animation duration in seconds - 
17444          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17445          * @type Number
17446          */
17447         animDuration: .4,
17448         
17449         /**
17450          * Manually trigger a cache refresh.
17451          */
17452         refreshCache : function(){
17453             for(var id in els){
17454                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17455                     els[id]._region = els[id].getRegion();
17456                 }
17457             }
17458         }
17459     };
17460 }();/*
17461  * Based on:
17462  * Ext JS Library 1.1.1
17463  * Copyright(c) 2006-2007, Ext JS, LLC.
17464  *
17465  * Originally Released Under LGPL - original licence link has changed is not relivant.
17466  *
17467  * Fork - LGPL
17468  * <script type="text/javascript">
17469  */
17470  
17471
17472 /**
17473  * @class Roo.dd.Registry
17474  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17475  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17476  * @singleton
17477  */
17478 Roo.dd.Registry = function(){
17479     var elements = {}; 
17480     var handles = {}; 
17481     var autoIdSeed = 0;
17482
17483     var getId = function(el, autogen){
17484         if(typeof el == "string"){
17485             return el;
17486         }
17487         var id = el.id;
17488         if(!id && autogen !== false){
17489             id = "roodd-" + (++autoIdSeed);
17490             el.id = id;
17491         }
17492         return id;
17493     };
17494     
17495     return {
17496     /**
17497      * Register a drag drop element
17498      * @param {String|HTMLElement} element The id or DOM node to register
17499      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17500      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17501      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17502      * populated in the data object (if applicable):
17503      * <pre>
17504 Value      Description<br />
17505 ---------  ------------------------------------------<br />
17506 handles    Array of DOM nodes that trigger dragging<br />
17507            for the element being registered<br />
17508 isHandle   True if the element passed in triggers<br />
17509            dragging itself, else false
17510 </pre>
17511      */
17512         register : function(el, data){
17513             data = data || {};
17514             if(typeof el == "string"){
17515                 el = document.getElementById(el);
17516             }
17517             data.ddel = el;
17518             elements[getId(el)] = data;
17519             if(data.isHandle !== false){
17520                 handles[data.ddel.id] = data;
17521             }
17522             if(data.handles){
17523                 var hs = data.handles;
17524                 for(var i = 0, len = hs.length; i < len; i++){
17525                         handles[getId(hs[i])] = data;
17526                 }
17527             }
17528         },
17529
17530     /**
17531      * Unregister a drag drop element
17532      * @param {String|HTMLElement}  element The id or DOM node to unregister
17533      */
17534         unregister : function(el){
17535             var id = getId(el, false);
17536             var data = elements[id];
17537             if(data){
17538                 delete elements[id];
17539                 if(data.handles){
17540                     var hs = data.handles;
17541                     for(var i = 0, len = hs.length; i < len; i++){
17542                         delete handles[getId(hs[i], false)];
17543                     }
17544                 }
17545             }
17546         },
17547
17548     /**
17549      * Returns the handle registered for a DOM Node by id
17550      * @param {String|HTMLElement} id The DOM node or id to look up
17551      * @return {Object} handle The custom handle data
17552      */
17553         getHandle : function(id){
17554             if(typeof id != "string"){ // must be element?
17555                 id = id.id;
17556             }
17557             return handles[id];
17558         },
17559
17560     /**
17561      * Returns the handle that is registered for the DOM node that is the target of the event
17562      * @param {Event} e The event
17563      * @return {Object} handle The custom handle data
17564      */
17565         getHandleFromEvent : function(e){
17566             var t = Roo.lib.Event.getTarget(e);
17567             return t ? handles[t.id] : null;
17568         },
17569
17570     /**
17571      * Returns a custom data object that is registered for a DOM node by id
17572      * @param {String|HTMLElement} id The DOM node or id to look up
17573      * @return {Object} data The custom data
17574      */
17575         getTarget : function(id){
17576             if(typeof id != "string"){ // must be element?
17577                 id = id.id;
17578             }
17579             return elements[id];
17580         },
17581
17582     /**
17583      * Returns a custom data object that is registered for the DOM node that is the target of the event
17584      * @param {Event} e The event
17585      * @return {Object} data The custom data
17586      */
17587         getTargetFromEvent : function(e){
17588             var t = Roo.lib.Event.getTarget(e);
17589             return t ? elements[t.id] || handles[t.id] : null;
17590         }
17591     };
17592 }();/*
17593  * Based on:
17594  * Ext JS Library 1.1.1
17595  * Copyright(c) 2006-2007, Ext JS, LLC.
17596  *
17597  * Originally Released Under LGPL - original licence link has changed is not relivant.
17598  *
17599  * Fork - LGPL
17600  * <script type="text/javascript">
17601  */
17602  
17603
17604 /**
17605  * @class Roo.dd.StatusProxy
17606  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17607  * default drag proxy used by all Roo.dd components.
17608  * @constructor
17609  * @param {Object} config
17610  */
17611 Roo.dd.StatusProxy = function(config){
17612     Roo.apply(this, config);
17613     this.id = this.id || Roo.id();
17614     this.el = new Roo.Layer({
17615         dh: {
17616             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17617                 {tag: "div", cls: "x-dd-drop-icon"},
17618                 {tag: "div", cls: "x-dd-drag-ghost"}
17619             ]
17620         }, 
17621         shadow: !config || config.shadow !== false
17622     });
17623     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17624     this.dropStatus = this.dropNotAllowed;
17625 };
17626
17627 Roo.dd.StatusProxy.prototype = {
17628     /**
17629      * @cfg {String} dropAllowed
17630      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17631      */
17632     dropAllowed : "x-dd-drop-ok",
17633     /**
17634      * @cfg {String} dropNotAllowed
17635      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17636      */
17637     dropNotAllowed : "x-dd-drop-nodrop",
17638
17639     /**
17640      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17641      * over the current target element.
17642      * @param {String} cssClass The css class for the new drop status indicator image
17643      */
17644     setStatus : function(cssClass){
17645         cssClass = cssClass || this.dropNotAllowed;
17646         if(this.dropStatus != cssClass){
17647             this.el.replaceClass(this.dropStatus, cssClass);
17648             this.dropStatus = cssClass;
17649         }
17650     },
17651
17652     /**
17653      * Resets the status indicator to the default dropNotAllowed value
17654      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17655      */
17656     reset : function(clearGhost){
17657         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17658         this.dropStatus = this.dropNotAllowed;
17659         if(clearGhost){
17660             this.ghost.update("");
17661         }
17662     },
17663
17664     /**
17665      * Updates the contents of the ghost element
17666      * @param {String} html The html that will replace the current innerHTML of the ghost element
17667      */
17668     update : function(html){
17669         if(typeof html == "string"){
17670             this.ghost.update(html);
17671         }else{
17672             this.ghost.update("");
17673             html.style.margin = "0";
17674             this.ghost.dom.appendChild(html);
17675         }
17676         // ensure float = none set?? cant remember why though.
17677         var el = this.ghost.dom.firstChild;
17678                 if(el){
17679                         Roo.fly(el).setStyle('float', 'none');
17680                 }
17681     },
17682     
17683     /**
17684      * Returns the underlying proxy {@link Roo.Layer}
17685      * @return {Roo.Layer} el
17686     */
17687     getEl : function(){
17688         return this.el;
17689     },
17690
17691     /**
17692      * Returns the ghost element
17693      * @return {Roo.Element} el
17694      */
17695     getGhost : function(){
17696         return this.ghost;
17697     },
17698
17699     /**
17700      * Hides the proxy
17701      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17702      */
17703     hide : function(clear){
17704         this.el.hide();
17705         if(clear){
17706             this.reset(true);
17707         }
17708     },
17709
17710     /**
17711      * Stops the repair animation if it's currently running
17712      */
17713     stop : function(){
17714         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17715             this.anim.stop();
17716         }
17717     },
17718
17719     /**
17720      * Displays this proxy
17721      */
17722     show : function(){
17723         this.el.show();
17724     },
17725
17726     /**
17727      * Force the Layer to sync its shadow and shim positions to the element
17728      */
17729     sync : function(){
17730         this.el.sync();
17731     },
17732
17733     /**
17734      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17735      * invalid drop operation by the item being dragged.
17736      * @param {Array} xy The XY position of the element ([x, y])
17737      * @param {Function} callback The function to call after the repair is complete
17738      * @param {Object} scope The scope in which to execute the callback
17739      */
17740     repair : function(xy, callback, scope){
17741         this.callback = callback;
17742         this.scope = scope;
17743         if(xy && this.animRepair !== false){
17744             this.el.addClass("x-dd-drag-repair");
17745             this.el.hideUnders(true);
17746             this.anim = this.el.shift({
17747                 duration: this.repairDuration || .5,
17748                 easing: 'easeOut',
17749                 xy: xy,
17750                 stopFx: true,
17751                 callback: this.afterRepair,
17752                 scope: this
17753             });
17754         }else{
17755             this.afterRepair();
17756         }
17757     },
17758
17759     // private
17760     afterRepair : function(){
17761         this.hide(true);
17762         if(typeof this.callback == "function"){
17763             this.callback.call(this.scope || this);
17764         }
17765         this.callback = null;
17766         this.scope = null;
17767     }
17768 };/*
17769  * Based on:
17770  * Ext JS Library 1.1.1
17771  * Copyright(c) 2006-2007, Ext JS, LLC.
17772  *
17773  * Originally Released Under LGPL - original licence link has changed is not relivant.
17774  *
17775  * Fork - LGPL
17776  * <script type="text/javascript">
17777  */
17778
17779 /**
17780  * @class Roo.dd.DragSource
17781  * @extends Roo.dd.DDProxy
17782  * A simple class that provides the basic implementation needed to make any element draggable.
17783  * @constructor
17784  * @param {String/HTMLElement/Element} el The container element
17785  * @param {Object} config
17786  */
17787 Roo.dd.DragSource = function(el, config){
17788     this.el = Roo.get(el);
17789     this.dragData = {};
17790     
17791     Roo.apply(this, config);
17792     
17793     if(!this.proxy){
17794         this.proxy = new Roo.dd.StatusProxy();
17795     }
17796
17797     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17798           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17799     
17800     this.dragging = false;
17801 };
17802
17803 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17804     /**
17805      * @cfg {String} dropAllowed
17806      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17807      */
17808     dropAllowed : "x-dd-drop-ok",
17809     /**
17810      * @cfg {String} dropNotAllowed
17811      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17812      */
17813     dropNotAllowed : "x-dd-drop-nodrop",
17814
17815     /**
17816      * Returns the data object associated with this drag source
17817      * @return {Object} data An object containing arbitrary data
17818      */
17819     getDragData : function(e){
17820         return this.dragData;
17821     },
17822
17823     // private
17824     onDragEnter : function(e, id){
17825         var target = Roo.dd.DragDropMgr.getDDById(id);
17826         this.cachedTarget = target;
17827         if(this.beforeDragEnter(target, e, id) !== false){
17828             if(target.isNotifyTarget){
17829                 var status = target.notifyEnter(this, e, this.dragData);
17830                 this.proxy.setStatus(status);
17831             }else{
17832                 this.proxy.setStatus(this.dropAllowed);
17833             }
17834             
17835             if(this.afterDragEnter){
17836                 /**
17837                  * An empty function by default, but provided so that you can perform a custom action
17838                  * when the dragged item enters the drop target by providing an implementation.
17839                  * @param {Roo.dd.DragDrop} target The drop target
17840                  * @param {Event} e The event object
17841                  * @param {String} id The id of the dragged element
17842                  * @method afterDragEnter
17843                  */
17844                 this.afterDragEnter(target, e, id);
17845             }
17846         }
17847     },
17848
17849     /**
17850      * An empty function by default, but provided so that you can perform a custom action
17851      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17852      * @param {Roo.dd.DragDrop} target The drop target
17853      * @param {Event} e The event object
17854      * @param {String} id The id of the dragged element
17855      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17856      */
17857     beforeDragEnter : function(target, e, id){
17858         return true;
17859     },
17860
17861     // private
17862     alignElWithMouse: function() {
17863         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17864         this.proxy.sync();
17865     },
17866
17867     // private
17868     onDragOver : function(e, id){
17869         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17870         if(this.beforeDragOver(target, e, id) !== false){
17871             if(target.isNotifyTarget){
17872                 var status = target.notifyOver(this, e, this.dragData);
17873                 this.proxy.setStatus(status);
17874             }
17875
17876             if(this.afterDragOver){
17877                 /**
17878                  * An empty function by default, but provided so that you can perform a custom action
17879                  * while the dragged item is over the drop target by providing an implementation.
17880                  * @param {Roo.dd.DragDrop} target The drop target
17881                  * @param {Event} e The event object
17882                  * @param {String} id The id of the dragged element
17883                  * @method afterDragOver
17884                  */
17885                 this.afterDragOver(target, e, id);
17886             }
17887         }
17888     },
17889
17890     /**
17891      * An empty function by default, but provided so that you can perform a custom action
17892      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17893      * @param {Roo.dd.DragDrop} target The drop target
17894      * @param {Event} e The event object
17895      * @param {String} id The id of the dragged element
17896      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17897      */
17898     beforeDragOver : function(target, e, id){
17899         return true;
17900     },
17901
17902     // private
17903     onDragOut : function(e, id){
17904         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17905         if(this.beforeDragOut(target, e, id) !== false){
17906             if(target.isNotifyTarget){
17907                 target.notifyOut(this, e, this.dragData);
17908             }
17909             this.proxy.reset();
17910             if(this.afterDragOut){
17911                 /**
17912                  * An empty function by default, but provided so that you can perform a custom action
17913                  * after the dragged item is dragged out of the target without dropping.
17914                  * @param {Roo.dd.DragDrop} target The drop target
17915                  * @param {Event} e The event object
17916                  * @param {String} id The id of the dragged element
17917                  * @method afterDragOut
17918                  */
17919                 this.afterDragOut(target, e, id);
17920             }
17921         }
17922         this.cachedTarget = null;
17923     },
17924
17925     /**
17926      * An empty function by default, but provided so that you can perform a custom action before the dragged
17927      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17928      * @param {Roo.dd.DragDrop} target The drop target
17929      * @param {Event} e The event object
17930      * @param {String} id The id of the dragged element
17931      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17932      */
17933     beforeDragOut : function(target, e, id){
17934         return true;
17935     },
17936     
17937     // private
17938     onDragDrop : function(e, id){
17939         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17940         if(this.beforeDragDrop(target, e, id) !== false){
17941             if(target.isNotifyTarget){
17942                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17943                     this.onValidDrop(target, e, id);
17944                 }else{
17945                     this.onInvalidDrop(target, e, id);
17946                 }
17947             }else{
17948                 this.onValidDrop(target, e, id);
17949             }
17950             
17951             if(this.afterDragDrop){
17952                 /**
17953                  * An empty function by default, but provided so that you can perform a custom action
17954                  * after a valid drag drop has occurred by providing an implementation.
17955                  * @param {Roo.dd.DragDrop} target The drop target
17956                  * @param {Event} e The event object
17957                  * @param {String} id The id of the dropped element
17958                  * @method afterDragDrop
17959                  */
17960                 this.afterDragDrop(target, e, id);
17961             }
17962         }
17963         delete this.cachedTarget;
17964     },
17965
17966     /**
17967      * An empty function by default, but provided so that you can perform a custom action before the dragged
17968      * item is dropped onto the target and optionally cancel the onDragDrop.
17969      * @param {Roo.dd.DragDrop} target The drop target
17970      * @param {Event} e The event object
17971      * @param {String} id The id of the dragged element
17972      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17973      */
17974     beforeDragDrop : function(target, e, id){
17975         return true;
17976     },
17977
17978     // private
17979     onValidDrop : function(target, e, id){
17980         this.hideProxy();
17981         if(this.afterValidDrop){
17982             /**
17983              * An empty function by default, but provided so that you can perform a custom action
17984              * after a valid drop has occurred by providing an implementation.
17985              * @param {Object} target The target DD 
17986              * @param {Event} e The event object
17987              * @param {String} id The id of the dropped element
17988              * @method afterInvalidDrop
17989              */
17990             this.afterValidDrop(target, e, id);
17991         }
17992     },
17993
17994     // private
17995     getRepairXY : function(e, data){
17996         return this.el.getXY();  
17997     },
17998
17999     // private
18000     onInvalidDrop : function(target, e, id){
18001         this.beforeInvalidDrop(target, e, id);
18002         if(this.cachedTarget){
18003             if(this.cachedTarget.isNotifyTarget){
18004                 this.cachedTarget.notifyOut(this, e, this.dragData);
18005             }
18006             this.cacheTarget = null;
18007         }
18008         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18009
18010         if(this.afterInvalidDrop){
18011             /**
18012              * An empty function by default, but provided so that you can perform a custom action
18013              * after an invalid drop has occurred by providing an implementation.
18014              * @param {Event} e The event object
18015              * @param {String} id The id of the dropped element
18016              * @method afterInvalidDrop
18017              */
18018             this.afterInvalidDrop(e, id);
18019         }
18020     },
18021
18022     // private
18023     afterRepair : function(){
18024         if(Roo.enableFx){
18025             this.el.highlight(this.hlColor || "c3daf9");
18026         }
18027         this.dragging = false;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action after an invalid
18032      * drop has occurred.
18033      * @param {Roo.dd.DragDrop} target The drop target
18034      * @param {Event} e The event object
18035      * @param {String} id The id of the dragged element
18036      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18037      */
18038     beforeInvalidDrop : function(target, e, id){
18039         return true;
18040     },
18041
18042     // private
18043     handleMouseDown : function(e){
18044         if(this.dragging) {
18045             return;
18046         }
18047         var data = this.getDragData(e);
18048         if(data && this.onBeforeDrag(data, e) !== false){
18049             this.dragData = data;
18050             this.proxy.stop();
18051             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18052         } 
18053     },
18054
18055     /**
18056      * An empty function by default, but provided so that you can perform a custom action before the initial
18057      * drag event begins and optionally cancel it.
18058      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18059      * @param {Event} e The event object
18060      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18061      */
18062     onBeforeDrag : function(data, e){
18063         return true;
18064     },
18065
18066     /**
18067      * An empty function by default, but provided so that you can perform a custom action once the initial
18068      * drag event has begun.  The drag cannot be canceled from this function.
18069      * @param {Number} x The x position of the click on the dragged object
18070      * @param {Number} y The y position of the click on the dragged object
18071      */
18072     onStartDrag : Roo.emptyFn,
18073
18074     // private - YUI override
18075     startDrag : function(x, y){
18076         this.proxy.reset();
18077         this.dragging = true;
18078         this.proxy.update("");
18079         this.onInitDrag(x, y);
18080         this.proxy.show();
18081     },
18082
18083     // private
18084     onInitDrag : function(x, y){
18085         var clone = this.el.dom.cloneNode(true);
18086         clone.id = Roo.id(); // prevent duplicate ids
18087         this.proxy.update(clone);
18088         this.onStartDrag(x, y);
18089         return true;
18090     },
18091
18092     /**
18093      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18094      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18095      */
18096     getProxy : function(){
18097         return this.proxy;  
18098     },
18099
18100     /**
18101      * Hides the drag source's {@link Roo.dd.StatusProxy}
18102      */
18103     hideProxy : function(){
18104         this.proxy.hide();  
18105         this.proxy.reset(true);
18106         this.dragging = false;
18107     },
18108
18109     // private
18110     triggerCacheRefresh : function(){
18111         Roo.dd.DDM.refreshCache(this.groups);
18112     },
18113
18114     // private - override to prevent hiding
18115     b4EndDrag: function(e) {
18116     },
18117
18118     // private - override to prevent moving
18119     endDrag : function(e){
18120         this.onEndDrag(this.dragData, e);
18121     },
18122
18123     // private
18124     onEndDrag : function(data, e){
18125     },
18126     
18127     // private - pin to cursor
18128     autoOffset : function(x, y) {
18129         this.setDelta(-12, -20);
18130     }    
18131 });/*
18132  * Based on:
18133  * Ext JS Library 1.1.1
18134  * Copyright(c) 2006-2007, Ext JS, LLC.
18135  *
18136  * Originally Released Under LGPL - original licence link has changed is not relivant.
18137  *
18138  * Fork - LGPL
18139  * <script type="text/javascript">
18140  */
18141
18142
18143 /**
18144  * @class Roo.dd.DropTarget
18145  * @extends Roo.dd.DDTarget
18146  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18147  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18148  * @constructor
18149  * @param {String/HTMLElement/Element} el The container element
18150  * @param {Object} config
18151  */
18152 Roo.dd.DropTarget = function(el, config){
18153     this.el = Roo.get(el);
18154     
18155     Roo.apply(this, config);
18156     
18157     if(this.containerScroll){
18158         Roo.dd.ScrollManager.register(this.el);
18159     }
18160     
18161     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18162           {isTarget: true});
18163
18164 };
18165
18166 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18167     /**
18168      * @cfg {String} overClass
18169      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18170      */
18171     /**
18172      * @cfg {String} dropAllowed
18173      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18174      */
18175     dropAllowed : "x-dd-drop-ok",
18176     /**
18177      * @cfg {String} dropNotAllowed
18178      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18179      */
18180     dropNotAllowed : "x-dd-drop-nodrop",
18181
18182     // private
18183     isTarget : true,
18184
18185     // private
18186     isNotifyTarget : true,
18187
18188     /**
18189      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18190      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18191      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18192      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18193      * @param {Event} e The event
18194      * @param {Object} data An object containing arbitrary data supplied by the drag source
18195      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18196      * underlying {@link Roo.dd.StatusProxy} can be updated
18197      */
18198     notifyEnter : function(dd, e, data){
18199         if(this.overClass){
18200             this.el.addClass(this.overClass);
18201         }
18202         return this.dropAllowed;
18203     },
18204
18205     /**
18206      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18207      * This method will be called on every mouse movement while the drag source is over the drop target.
18208      * This default implementation simply returns the dropAllowed config value.
18209      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18210      * @param {Event} e The event
18211      * @param {Object} data An object containing arbitrary data supplied by the drag source
18212      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18213      * underlying {@link Roo.dd.StatusProxy} can be updated
18214      */
18215     notifyOver : function(dd, e, data){
18216         return this.dropAllowed;
18217     },
18218
18219     /**
18220      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18221      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18222      * overClass (if any) from the drop element.
18223      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18224      * @param {Event} e The event
18225      * @param {Object} data An object containing arbitrary data supplied by the drag source
18226      */
18227     notifyOut : function(dd, e, data){
18228         if(this.overClass){
18229             this.el.removeClass(this.overClass);
18230         }
18231     },
18232
18233     /**
18234      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18235      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18236      * implementation that does something to process the drop event and returns true so that the drag source's
18237      * repair action does not run.
18238      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18239      * @param {Event} e The event
18240      * @param {Object} data An object containing arbitrary data supplied by the drag source
18241      * @return {Boolean} True if the drop was valid, else false
18242      */
18243     notifyDrop : function(dd, e, data){
18244         return false;
18245     }
18246 });/*
18247  * Based on:
18248  * Ext JS Library 1.1.1
18249  * Copyright(c) 2006-2007, Ext JS, LLC.
18250  *
18251  * Originally Released Under LGPL - original licence link has changed is not relivant.
18252  *
18253  * Fork - LGPL
18254  * <script type="text/javascript">
18255  */
18256
18257
18258 /**
18259  * @class Roo.dd.DragZone
18260  * @extends Roo.dd.DragSource
18261  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18262  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18263  * @constructor
18264  * @param {String/HTMLElement/Element} el The container element
18265  * @param {Object} config
18266  */
18267 Roo.dd.DragZone = function(el, config){
18268     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18269     if(this.containerScroll){
18270         Roo.dd.ScrollManager.register(this.el);
18271     }
18272 };
18273
18274 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18275     /**
18276      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18277      * for auto scrolling during drag operations.
18278      */
18279     /**
18280      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18281      * method after a failed drop (defaults to "c3daf9" - light blue)
18282      */
18283
18284     /**
18285      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18286      * for a valid target to drag based on the mouse down. Override this method
18287      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18288      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18289      * @param {EventObject} e The mouse down event
18290      * @return {Object} The dragData
18291      */
18292     getDragData : function(e){
18293         return Roo.dd.Registry.getHandleFromEvent(e);
18294     },
18295     
18296     /**
18297      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18298      * this.dragData.ddel
18299      * @param {Number} x The x position of the click on the dragged object
18300      * @param {Number} y The y position of the click on the dragged object
18301      * @return {Boolean} true to continue the drag, false to cancel
18302      */
18303     onInitDrag : function(x, y){
18304         this.proxy.update(this.dragData.ddel.cloneNode(true));
18305         this.onStartDrag(x, y);
18306         return true;
18307     },
18308     
18309     /**
18310      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18311      */
18312     afterRepair : function(){
18313         if(Roo.enableFx){
18314             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18315         }
18316         this.dragging = false;
18317     },
18318
18319     /**
18320      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18321      * the XY of this.dragData.ddel
18322      * @param {EventObject} e The mouse up event
18323      * @return {Array} The xy location (e.g. [100, 200])
18324      */
18325     getRepairXY : function(e){
18326         return Roo.Element.fly(this.dragData.ddel).getXY();  
18327     }
18328 });/*
18329  * Based on:
18330  * Ext JS Library 1.1.1
18331  * Copyright(c) 2006-2007, Ext JS, LLC.
18332  *
18333  * Originally Released Under LGPL - original licence link has changed is not relivant.
18334  *
18335  * Fork - LGPL
18336  * <script type="text/javascript">
18337  */
18338 /**
18339  * @class Roo.dd.DropZone
18340  * @extends Roo.dd.DropTarget
18341  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18342  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18343  * @constructor
18344  * @param {String/HTMLElement/Element} el The container element
18345  * @param {Object} config
18346  */
18347 Roo.dd.DropZone = function(el, config){
18348     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18349 };
18350
18351 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18352     /**
18353      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18354      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18355      * provide your own custom lookup.
18356      * @param {Event} e The event
18357      * @return {Object} data The custom data
18358      */
18359     getTargetFromEvent : function(e){
18360         return Roo.dd.Registry.getTargetFromEvent(e);
18361     },
18362
18363     /**
18364      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18365      * that it has registered.  This method has no default implementation and should be overridden to provide
18366      * node-specific processing if necessary.
18367      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18368      * {@link #getTargetFromEvent} for this node)
18369      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18370      * @param {Event} e The event
18371      * @param {Object} data An object containing arbitrary data supplied by the drag source
18372      */
18373     onNodeEnter : function(n, dd, e, data){
18374         
18375     },
18376
18377     /**
18378      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18379      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18380      * overridden to provide the proper feedback.
18381      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18382      * {@link #getTargetFromEvent} for this node)
18383      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18384      * @param {Event} e The event
18385      * @param {Object} data An object containing arbitrary data supplied by the drag source
18386      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18387      * underlying {@link Roo.dd.StatusProxy} can be updated
18388      */
18389     onNodeOver : function(n, dd, e, data){
18390         return this.dropAllowed;
18391     },
18392
18393     /**
18394      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18395      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18396      * node-specific processing if necessary.
18397      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18398      * {@link #getTargetFromEvent} for this node)
18399      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18400      * @param {Event} e The event
18401      * @param {Object} data An object containing arbitrary data supplied by the drag source
18402      */
18403     onNodeOut : function(n, dd, e, data){
18404         
18405     },
18406
18407     /**
18408      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18409      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18410      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18411      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18412      * {@link #getTargetFromEvent} for this node)
18413      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18414      * @param {Event} e The event
18415      * @param {Object} data An object containing arbitrary data supplied by the drag source
18416      * @return {Boolean} True if the drop was valid, else false
18417      */
18418     onNodeDrop : function(n, dd, e, data){
18419         return false;
18420     },
18421
18422     /**
18423      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18424      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18425      * it should be overridden to provide the proper feedback if necessary.
18426      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18427      * @param {Event} e The event
18428      * @param {Object} data An object containing arbitrary data supplied by the drag source
18429      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18430      * underlying {@link Roo.dd.StatusProxy} can be updated
18431      */
18432     onContainerOver : function(dd, e, data){
18433         return this.dropNotAllowed;
18434     },
18435
18436     /**
18437      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18438      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18439      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18440      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18441      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18442      * @param {Event} e The event
18443      * @param {Object} data An object containing arbitrary data supplied by the drag source
18444      * @return {Boolean} True if the drop was valid, else false
18445      */
18446     onContainerDrop : function(dd, e, data){
18447         return false;
18448     },
18449
18450     /**
18451      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18452      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18453      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18454      * you should override this method and provide a custom implementation.
18455      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18456      * @param {Event} e The event
18457      * @param {Object} data An object containing arbitrary data supplied by the drag source
18458      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18459      * underlying {@link Roo.dd.StatusProxy} can be updated
18460      */
18461     notifyEnter : function(dd, e, data){
18462         return this.dropNotAllowed;
18463     },
18464
18465     /**
18466      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18467      * This method will be called on every mouse movement while the drag source is over the drop zone.
18468      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18469      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18470      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18471      * registered node, it will call {@link #onContainerOver}.
18472      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18473      * @param {Event} e The event
18474      * @param {Object} data An object containing arbitrary data supplied by the drag source
18475      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18476      * underlying {@link Roo.dd.StatusProxy} can be updated
18477      */
18478     notifyOver : function(dd, e, data){
18479         var n = this.getTargetFromEvent(e);
18480         if(!n){ // not over valid drop target
18481             if(this.lastOverNode){
18482                 this.onNodeOut(this.lastOverNode, dd, e, data);
18483                 this.lastOverNode = null;
18484             }
18485             return this.onContainerOver(dd, e, data);
18486         }
18487         if(this.lastOverNode != n){
18488             if(this.lastOverNode){
18489                 this.onNodeOut(this.lastOverNode, dd, e, data);
18490             }
18491             this.onNodeEnter(n, dd, e, data);
18492             this.lastOverNode = n;
18493         }
18494         return this.onNodeOver(n, dd, e, data);
18495     },
18496
18497     /**
18498      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18499      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18500      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18501      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18502      * @param {Event} e The event
18503      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18504      */
18505     notifyOut : function(dd, e, data){
18506         if(this.lastOverNode){
18507             this.onNodeOut(this.lastOverNode, dd, e, data);
18508             this.lastOverNode = null;
18509         }
18510     },
18511
18512     /**
18513      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18514      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18515      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18516      * otherwise it will call {@link #onContainerDrop}.
18517      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18518      * @param {Event} e The event
18519      * @param {Object} data An object containing arbitrary data supplied by the drag source
18520      * @return {Boolean} True if the drop was valid, else false
18521      */
18522     notifyDrop : function(dd, e, data){
18523         if(this.lastOverNode){
18524             this.onNodeOut(this.lastOverNode, dd, e, data);
18525             this.lastOverNode = null;
18526         }
18527         var n = this.getTargetFromEvent(e);
18528         return n ?
18529             this.onNodeDrop(n, dd, e, data) :
18530             this.onContainerDrop(dd, e, data);
18531     },
18532
18533     // private
18534     triggerCacheRefresh : function(){
18535         Roo.dd.DDM.refreshCache(this.groups);
18536     }  
18537 });/*
18538  * Based on:
18539  * Ext JS Library 1.1.1
18540  * Copyright(c) 2006-2007, Ext JS, LLC.
18541  *
18542  * Originally Released Under LGPL - original licence link has changed is not relivant.
18543  *
18544  * Fork - LGPL
18545  * <script type="text/javascript">
18546  */
18547
18548
18549 /**
18550  * @class Roo.data.SortTypes
18551  * @singleton
18552  * Defines the default sorting (casting?) comparison functions used when sorting data.
18553  */
18554 Roo.data.SortTypes = {
18555     /**
18556      * Default sort that does nothing
18557      * @param {Mixed} s The value being converted
18558      * @return {Mixed} The comparison value
18559      */
18560     none : function(s){
18561         return s;
18562     },
18563     
18564     /**
18565      * The regular expression used to strip tags
18566      * @type {RegExp}
18567      * @property
18568      */
18569     stripTagsRE : /<\/?[^>]+>/gi,
18570     
18571     /**
18572      * Strips all HTML tags to sort on text only
18573      * @param {Mixed} s The value being converted
18574      * @return {String} The comparison value
18575      */
18576     asText : function(s){
18577         return String(s).replace(this.stripTagsRE, "");
18578     },
18579     
18580     /**
18581      * Strips all HTML tags to sort on text only - Case insensitive
18582      * @param {Mixed} s The value being converted
18583      * @return {String} The comparison value
18584      */
18585     asUCText : function(s){
18586         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18587     },
18588     
18589     /**
18590      * Case insensitive string
18591      * @param {Mixed} s The value being converted
18592      * @return {String} The comparison value
18593      */
18594     asUCString : function(s) {
18595         return String(s).toUpperCase();
18596     },
18597     
18598     /**
18599      * Date sorting
18600      * @param {Mixed} s The value being converted
18601      * @return {Number} The comparison value
18602      */
18603     asDate : function(s) {
18604         if(!s){
18605             return 0;
18606         }
18607         if(s instanceof Date){
18608             return s.getTime();
18609         }
18610         return Date.parse(String(s));
18611     },
18612     
18613     /**
18614      * Float sorting
18615      * @param {Mixed} s The value being converted
18616      * @return {Float} The comparison value
18617      */
18618     asFloat : function(s) {
18619         var val = parseFloat(String(s).replace(/,/g, ""));
18620         if(isNaN(val)) val = 0;
18621         return val;
18622     },
18623     
18624     /**
18625      * Integer sorting
18626      * @param {Mixed} s The value being converted
18627      * @return {Number} The comparison value
18628      */
18629     asInt : function(s) {
18630         var val = parseInt(String(s).replace(/,/g, ""));
18631         if(isNaN(val)) val = 0;
18632         return val;
18633     }
18634 };/*
18635  * Based on:
18636  * Ext JS Library 1.1.1
18637  * Copyright(c) 2006-2007, Ext JS, LLC.
18638  *
18639  * Originally Released Under LGPL - original licence link has changed is not relivant.
18640  *
18641  * Fork - LGPL
18642  * <script type="text/javascript">
18643  */
18644
18645 /**
18646 * @class Roo.data.Record
18647  * Instances of this class encapsulate both record <em>definition</em> information, and record
18648  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18649  * to access Records cached in an {@link Roo.data.Store} object.<br>
18650  * <p>
18651  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18652  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18653  * objects.<br>
18654  * <p>
18655  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18656  * @constructor
18657  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18658  * {@link #create}. The parameters are the same.
18659  * @param {Array} data An associative Array of data values keyed by the field name.
18660  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18661  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18662  * not specified an integer id is generated.
18663  */
18664 Roo.data.Record = function(data, id){
18665     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18666     this.data = data;
18667 };
18668
18669 /**
18670  * Generate a constructor for a specific record layout.
18671  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18672  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18673  * Each field definition object may contain the following properties: <ul>
18674  * <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,
18675  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18676  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18677  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18678  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18679  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18680  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18681  * this may be omitted.</p></li>
18682  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18683  * <ul><li>auto (Default, implies no conversion)</li>
18684  * <li>string</li>
18685  * <li>int</li>
18686  * <li>float</li>
18687  * <li>boolean</li>
18688  * <li>date</li></ul></p></li>
18689  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18690  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18691  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18692  * by the Reader into an object that will be stored in the Record. It is passed the
18693  * following parameters:<ul>
18694  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18695  * </ul></p></li>
18696  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18697  * </ul>
18698  * <br>usage:<br><pre><code>
18699 var TopicRecord = Roo.data.Record.create(
18700     {name: 'title', mapping: 'topic_title'},
18701     {name: 'author', mapping: 'username'},
18702     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18703     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18704     {name: 'lastPoster', mapping: 'user2'},
18705     {name: 'excerpt', mapping: 'post_text'}
18706 );
18707
18708 var myNewRecord = new TopicRecord({
18709     title: 'Do my job please',
18710     author: 'noobie',
18711     totalPosts: 1,
18712     lastPost: new Date(),
18713     lastPoster: 'Animal',
18714     excerpt: 'No way dude!'
18715 });
18716 myStore.add(myNewRecord);
18717 </code></pre>
18718  * @method create
18719  * @static
18720  */
18721 Roo.data.Record.create = function(o){
18722     var f = function(){
18723         f.superclass.constructor.apply(this, arguments);
18724     };
18725     Roo.extend(f, Roo.data.Record);
18726     var p = f.prototype;
18727     p.fields = new Roo.util.MixedCollection(false, function(field){
18728         return field.name;
18729     });
18730     for(var i = 0, len = o.length; i < len; i++){
18731         p.fields.add(new Roo.data.Field(o[i]));
18732     }
18733     f.getField = function(name){
18734         return p.fields.get(name);  
18735     };
18736     return f;
18737 };
18738
18739 Roo.data.Record.AUTO_ID = 1000;
18740 Roo.data.Record.EDIT = 'edit';
18741 Roo.data.Record.REJECT = 'reject';
18742 Roo.data.Record.COMMIT = 'commit';
18743
18744 Roo.data.Record.prototype = {
18745     /**
18746      * Readonly flag - true if this record has been modified.
18747      * @type Boolean
18748      */
18749     dirty : false,
18750     editing : false,
18751     error: null,
18752     modified: null,
18753
18754     // private
18755     join : function(store){
18756         this.store = store;
18757     },
18758
18759     /**
18760      * Set the named field to the specified value.
18761      * @param {String} name The name of the field to set.
18762      * @param {Object} value The value to set the field to.
18763      */
18764     set : function(name, value){
18765         if(this.data[name] == value){
18766             return;
18767         }
18768         this.dirty = true;
18769         if(!this.modified){
18770             this.modified = {};
18771         }
18772         if(typeof this.modified[name] == 'undefined'){
18773             this.modified[name] = this.data[name];
18774         }
18775         this.data[name] = value;
18776         if(!this.editing){
18777             this.store.afterEdit(this);
18778         }       
18779     },
18780
18781     /**
18782      * Get the value of the named field.
18783      * @param {String} name The name of the field to get the value of.
18784      * @return {Object} The value of the field.
18785      */
18786     get : function(name){
18787         return this.data[name]; 
18788     },
18789
18790     // private
18791     beginEdit : function(){
18792         this.editing = true;
18793         this.modified = {}; 
18794     },
18795
18796     // private
18797     cancelEdit : function(){
18798         this.editing = false;
18799         delete this.modified;
18800     },
18801
18802     // private
18803     endEdit : function(){
18804         this.editing = false;
18805         if(this.dirty && this.store){
18806             this.store.afterEdit(this);
18807         }
18808     },
18809
18810     /**
18811      * Usually called by the {@link Roo.data.Store} which owns the Record.
18812      * Rejects all changes made to the Record since either creation, or the last commit operation.
18813      * Modified fields are reverted to their original values.
18814      * <p>
18815      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18816      * of reject operations.
18817      */
18818     reject : function(){
18819         var m = this.modified;
18820         for(var n in m){
18821             if(typeof m[n] != "function"){
18822                 this.data[n] = m[n];
18823             }
18824         }
18825         this.dirty = false;
18826         delete this.modified;
18827         this.editing = false;
18828         if(this.store){
18829             this.store.afterReject(this);
18830         }
18831     },
18832
18833     /**
18834      * Usually called by the {@link Roo.data.Store} which owns the Record.
18835      * Commits all changes made to the Record since either creation, or the last commit operation.
18836      * <p>
18837      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18838      * of commit operations.
18839      */
18840     commit : function(){
18841         this.dirty = false;
18842         delete this.modified;
18843         this.editing = false;
18844         if(this.store){
18845             this.store.afterCommit(this);
18846         }
18847     },
18848
18849     // private
18850     hasError : function(){
18851         return this.error != null;
18852     },
18853
18854     // private
18855     clearError : function(){
18856         this.error = null;
18857     },
18858
18859     /**
18860      * Creates a copy of this record.
18861      * @param {String} id (optional) A new record id if you don't want to use this record's id
18862      * @return {Record}
18863      */
18864     copy : function(newId) {
18865         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18866     }
18867 };/*
18868  * Based on:
18869  * Ext JS Library 1.1.1
18870  * Copyright(c) 2006-2007, Ext JS, LLC.
18871  *
18872  * Originally Released Under LGPL - original licence link has changed is not relivant.
18873  *
18874  * Fork - LGPL
18875  * <script type="text/javascript">
18876  */
18877
18878
18879
18880 /**
18881  * @class Roo.data.Store
18882  * @extends Roo.util.Observable
18883  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18884  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18885  * <p>
18886  * 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
18887  * has no knowledge of the format of the data returned by the Proxy.<br>
18888  * <p>
18889  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18890  * instances from the data object. These records are cached and made available through accessor functions.
18891  * @constructor
18892  * Creates a new Store.
18893  * @param {Object} config A config object containing the objects needed for the Store to access data,
18894  * and read the data into Records.
18895  */
18896 Roo.data.Store = function(config){
18897     this.data = new Roo.util.MixedCollection(false);
18898     this.data.getKey = function(o){
18899         return o.id;
18900     };
18901     this.baseParams = {};
18902     // private
18903     this.paramNames = {
18904         "start" : "start",
18905         "limit" : "limit",
18906         "sort" : "sort",
18907         "dir" : "dir"
18908     };
18909
18910     if(config && config.data){
18911         this.inlineData = config.data;
18912         delete config.data;
18913     }
18914
18915     Roo.apply(this, config);
18916     
18917     if(this.reader){ // reader passed
18918         this.reader = Roo.factory(this.reader, Roo.data);
18919         this.reader.xmodule = this.xmodule || false;
18920         if(!this.recordType){
18921             this.recordType = this.reader.recordType;
18922         }
18923         if(this.reader.onMetaChange){
18924             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18925         }
18926     }
18927
18928     if(this.recordType){
18929         this.fields = this.recordType.prototype.fields;
18930     }
18931     this.modified = [];
18932
18933     this.addEvents({
18934         /**
18935          * @event datachanged
18936          * Fires when the data cache has changed, and a widget which is using this Store
18937          * as a Record cache should refresh its view.
18938          * @param {Store} this
18939          */
18940         datachanged : true,
18941         /**
18942          * @event metachange
18943          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18944          * @param {Store} this
18945          * @param {Object} meta The JSON metadata
18946          */
18947         metachange : true,
18948         /**
18949          * @event add
18950          * Fires when Records have been added to the Store
18951          * @param {Store} this
18952          * @param {Roo.data.Record[]} records The array of Records added
18953          * @param {Number} index The index at which the record(s) were added
18954          */
18955         add : true,
18956         /**
18957          * @event remove
18958          * Fires when a Record has been removed from the Store
18959          * @param {Store} this
18960          * @param {Roo.data.Record} record The Record that was removed
18961          * @param {Number} index The index at which the record was removed
18962          */
18963         remove : true,
18964         /**
18965          * @event update
18966          * Fires when a Record has been updated
18967          * @param {Store} this
18968          * @param {Roo.data.Record} record The Record that was updated
18969          * @param {String} operation The update operation being performed.  Value may be one of:
18970          * <pre><code>
18971  Roo.data.Record.EDIT
18972  Roo.data.Record.REJECT
18973  Roo.data.Record.COMMIT
18974          * </code></pre>
18975          */
18976         update : true,
18977         /**
18978          * @event clear
18979          * Fires when the data cache has been cleared.
18980          * @param {Store} this
18981          */
18982         clear : true,
18983         /**
18984          * @event beforeload
18985          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18986          * the load action will be canceled.
18987          * @param {Store} this
18988          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18989          */
18990         beforeload : true,
18991         /**
18992          * @event load
18993          * Fires after a new set of Records has been loaded.
18994          * @param {Store} this
18995          * @param {Roo.data.Record[]} records The Records that were loaded
18996          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18997          */
18998         load : true,
18999         /**
19000          * @event loadexception
19001          * Fires if an exception occurs in the Proxy during loading.
19002          * Called with the signature of the Proxy's "loadexception" event.
19003          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19004          * 
19005          * @param {Proxy} 
19006          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19007          * @param {Object} load options 
19008          * @param {Object} jsonData from your request (normally this contains the Exception)
19009          */
19010         loadexception : true
19011     });
19012     
19013     if(this.proxy){
19014         this.proxy = Roo.factory(this.proxy, Roo.data);
19015         this.proxy.xmodule = this.xmodule || false;
19016         this.relayEvents(this.proxy,  ["loadexception"]);
19017     }
19018     this.sortToggle = {};
19019
19020     Roo.data.Store.superclass.constructor.call(this);
19021
19022     if(this.inlineData){
19023         this.loadData(this.inlineData);
19024         delete this.inlineData;
19025     }
19026 };
19027 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19028      /**
19029     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19030     * without a remote query - used by combo/forms at present.
19031     */
19032     
19033     /**
19034     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19035     */
19036     /**
19037     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19038     */
19039     /**
19040     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19041     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19042     */
19043     /**
19044     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19045     * on any HTTP request
19046     */
19047     /**
19048     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19049     */
19050     /**
19051     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19052     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19053     */
19054     remoteSort : false,
19055
19056     /**
19057     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19058      * loaded or when a record is removed. (defaults to false).
19059     */
19060     pruneModifiedRecords : false,
19061
19062     // private
19063     lastOptions : null,
19064
19065     /**
19066      * Add Records to the Store and fires the add event.
19067      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19068      */
19069     add : function(records){
19070         records = [].concat(records);
19071         for(var i = 0, len = records.length; i < len; i++){
19072             records[i].join(this);
19073         }
19074         var index = this.data.length;
19075         this.data.addAll(records);
19076         this.fireEvent("add", this, records, index);
19077     },
19078
19079     /**
19080      * Remove a Record from the Store and fires the remove event.
19081      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19082      */
19083     remove : function(record){
19084         var index = this.data.indexOf(record);
19085         this.data.removeAt(index);
19086         if(this.pruneModifiedRecords){
19087             this.modified.remove(record);
19088         }
19089         this.fireEvent("remove", this, record, index);
19090     },
19091
19092     /**
19093      * Remove all Records from the Store and fires the clear event.
19094      */
19095     removeAll : function(){
19096         this.data.clear();
19097         if(this.pruneModifiedRecords){
19098             this.modified = [];
19099         }
19100         this.fireEvent("clear", this);
19101     },
19102
19103     /**
19104      * Inserts Records to the Store at the given index and fires the add event.
19105      * @param {Number} index The start index at which to insert the passed Records.
19106      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19107      */
19108     insert : function(index, records){
19109         records = [].concat(records);
19110         for(var i = 0, len = records.length; i < len; i++){
19111             this.data.insert(index, records[i]);
19112             records[i].join(this);
19113         }
19114         this.fireEvent("add", this, records, index);
19115     },
19116
19117     /**
19118      * Get the index within the cache of the passed Record.
19119      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19120      * @return {Number} The index of the passed Record. Returns -1 if not found.
19121      */
19122     indexOf : function(record){
19123         return this.data.indexOf(record);
19124     },
19125
19126     /**
19127      * Get the index within the cache of the Record with the passed id.
19128      * @param {String} id The id of the Record to find.
19129      * @return {Number} The index of the Record. Returns -1 if not found.
19130      */
19131     indexOfId : function(id){
19132         return this.data.indexOfKey(id);
19133     },
19134
19135     /**
19136      * Get the Record with the specified id.
19137      * @param {String} id The id of the Record to find.
19138      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19139      */
19140     getById : function(id){
19141         return this.data.key(id);
19142     },
19143
19144     /**
19145      * Get the Record at the specified index.
19146      * @param {Number} index The index of the Record to find.
19147      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19148      */
19149     getAt : function(index){
19150         return this.data.itemAt(index);
19151     },
19152
19153     /**
19154      * Returns a range of Records between specified indices.
19155      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19156      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19157      * @return {Roo.data.Record[]} An array of Records
19158      */
19159     getRange : function(start, end){
19160         return this.data.getRange(start, end);
19161     },
19162
19163     // private
19164     storeOptions : function(o){
19165         o = Roo.apply({}, o);
19166         delete o.callback;
19167         delete o.scope;
19168         this.lastOptions = o;
19169     },
19170
19171     /**
19172      * Loads the Record cache from the configured Proxy using the configured Reader.
19173      * <p>
19174      * If using remote paging, then the first load call must specify the <em>start</em>
19175      * and <em>limit</em> properties in the options.params property to establish the initial
19176      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19177      * <p>
19178      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19179      * and this call will return before the new data has been loaded. Perform any post-processing
19180      * in a callback function, or in a "load" event handler.</strong>
19181      * <p>
19182      * @param {Object} options An object containing properties which control loading options:<ul>
19183      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19184      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19185      * passed the following arguments:<ul>
19186      * <li>r : Roo.data.Record[]</li>
19187      * <li>options: Options object from the load call</li>
19188      * <li>success: Boolean success indicator</li></ul></li>
19189      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19190      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19191      * </ul>
19192      */
19193     load : function(options){
19194         options = options || {};
19195         if(this.fireEvent("beforeload", this, options) !== false){
19196             this.storeOptions(options);
19197             var p = Roo.apply(options.params || {}, this.baseParams);
19198             if(this.sortInfo && this.remoteSort){
19199                 var pn = this.paramNames;
19200                 p[pn["sort"]] = this.sortInfo.field;
19201                 p[pn["dir"]] = this.sortInfo.direction;
19202             }
19203             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19204         }
19205     },
19206
19207     /**
19208      * Reloads the Record cache from the configured Proxy using the configured Reader and
19209      * the options from the last load operation performed.
19210      * @param {Object} options (optional) An object containing properties which may override the options
19211      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19212      * the most recently used options are reused).
19213      */
19214     reload : function(options){
19215         this.load(Roo.applyIf(options||{}, this.lastOptions));
19216     },
19217
19218     // private
19219     // Called as a callback by the Reader during a load operation.
19220     loadRecords : function(o, options, success){
19221         if(!o || success === false){
19222             if(success !== false){
19223                 this.fireEvent("load", this, [], options);
19224             }
19225             if(options.callback){
19226                 options.callback.call(options.scope || this, [], options, false);
19227             }
19228             return;
19229         }
19230         // if data returned failure - throw an exception.
19231         if (o.success === false) {
19232             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19233             return;
19234         }
19235         var r = o.records, t = o.totalRecords || r.length;
19236         if(!options || options.add !== true){
19237             if(this.pruneModifiedRecords){
19238                 this.modified = [];
19239             }
19240             for(var i = 0, len = r.length; i < len; i++){
19241                 r[i].join(this);
19242             }
19243             if(this.snapshot){
19244                 this.data = this.snapshot;
19245                 delete this.snapshot;
19246             }
19247             this.data.clear();
19248             this.data.addAll(r);
19249             this.totalLength = t;
19250             this.applySort();
19251             this.fireEvent("datachanged", this);
19252         }else{
19253             this.totalLength = Math.max(t, this.data.length+r.length);
19254             this.add(r);
19255         }
19256         this.fireEvent("load", this, r, options);
19257         if(options.callback){
19258             options.callback.call(options.scope || this, r, options, true);
19259         }
19260     },
19261
19262     /**
19263      * Loads data from a passed data block. A Reader which understands the format of the data
19264      * must have been configured in the constructor.
19265      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19266      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19267      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19268      */
19269     loadData : function(o, append){
19270         var r = this.reader.readRecords(o);
19271         this.loadRecords(r, {add: append}, true);
19272     },
19273
19274     /**
19275      * Gets the number of cached records.
19276      * <p>
19277      * <em>If using paging, this may not be the total size of the dataset. If the data object
19278      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19279      * the data set size</em>
19280      */
19281     getCount : function(){
19282         return this.data.length || 0;
19283     },
19284
19285     /**
19286      * Gets the total number of records in the dataset as returned by the server.
19287      * <p>
19288      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19289      * the dataset size</em>
19290      */
19291     getTotalCount : function(){
19292         return this.totalLength || 0;
19293     },
19294
19295     /**
19296      * Returns the sort state of the Store as an object with two properties:
19297      * <pre><code>
19298  field {String} The name of the field by which the Records are sorted
19299  direction {String} The sort order, "ASC" or "DESC"
19300      * </code></pre>
19301      */
19302     getSortState : function(){
19303         return this.sortInfo;
19304     },
19305
19306     // private
19307     applySort : function(){
19308         if(this.sortInfo && !this.remoteSort){
19309             var s = this.sortInfo, f = s.field;
19310             var st = this.fields.get(f).sortType;
19311             var fn = function(r1, r2){
19312                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19313                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19314             };
19315             this.data.sort(s.direction, fn);
19316             if(this.snapshot && this.snapshot != this.data){
19317                 this.snapshot.sort(s.direction, fn);
19318             }
19319         }
19320     },
19321
19322     /**
19323      * Sets the default sort column and order to be used by the next load operation.
19324      * @param {String} fieldName The name of the field to sort by.
19325      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19326      */
19327     setDefaultSort : function(field, dir){
19328         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19329     },
19330
19331     /**
19332      * Sort the Records.
19333      * If remote sorting is used, the sort is performed on the server, and the cache is
19334      * reloaded. If local sorting is used, the cache is sorted internally.
19335      * @param {String} fieldName The name of the field to sort by.
19336      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19337      */
19338     sort : function(fieldName, dir){
19339         var f = this.fields.get(fieldName);
19340         if(!dir){
19341             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19342                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19343             }else{
19344                 dir = f.sortDir;
19345             }
19346         }
19347         this.sortToggle[f.name] = dir;
19348         this.sortInfo = {field: f.name, direction: dir};
19349         if(!this.remoteSort){
19350             this.applySort();
19351             this.fireEvent("datachanged", this);
19352         }else{
19353             this.load(this.lastOptions);
19354         }
19355     },
19356
19357     /**
19358      * Calls the specified function for each of the Records in the cache.
19359      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19360      * Returning <em>false</em> aborts and exits the iteration.
19361      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19362      */
19363     each : function(fn, scope){
19364         this.data.each(fn, scope);
19365     },
19366
19367     /**
19368      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19369      * (e.g., during paging).
19370      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19371      */
19372     getModifiedRecords : function(){
19373         return this.modified;
19374     },
19375
19376     // private
19377     createFilterFn : function(property, value, anyMatch){
19378         if(!value.exec){ // not a regex
19379             value = String(value);
19380             if(value.length == 0){
19381                 return false;
19382             }
19383             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19384         }
19385         return function(r){
19386             return value.test(r.data[property]);
19387         };
19388     },
19389
19390     /**
19391      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19392      * @param {String} property A field on your records
19393      * @param {Number} start The record index to start at (defaults to 0)
19394      * @param {Number} end The last record index to include (defaults to length - 1)
19395      * @return {Number} The sum
19396      */
19397     sum : function(property, start, end){
19398         var rs = this.data.items, v = 0;
19399         start = start || 0;
19400         end = (end || end === 0) ? end : rs.length-1;
19401
19402         for(var i = start; i <= end; i++){
19403             v += (rs[i].data[property] || 0);
19404         }
19405         return v;
19406     },
19407
19408     /**
19409      * Filter the records by a specified property.
19410      * @param {String} field A field on your records
19411      * @param {String/RegExp} value Either a string that the field
19412      * should start with or a RegExp to test against the field
19413      * @param {Boolean} anyMatch True to match any part not just the beginning
19414      */
19415     filter : function(property, value, anyMatch){
19416         var fn = this.createFilterFn(property, value, anyMatch);
19417         return fn ? this.filterBy(fn) : this.clearFilter();
19418     },
19419
19420     /**
19421      * Filter by a function. The specified function will be called with each
19422      * record in this data source. If the function returns true the record is included,
19423      * otherwise it is filtered.
19424      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19425      * @param {Object} scope (optional) The scope of the function (defaults to this)
19426      */
19427     filterBy : function(fn, scope){
19428         this.snapshot = this.snapshot || this.data;
19429         this.data = this.queryBy(fn, scope||this);
19430         this.fireEvent("datachanged", this);
19431     },
19432
19433     /**
19434      * Query the records by a specified property.
19435      * @param {String} field A field on your records
19436      * @param {String/RegExp} value Either a string that the field
19437      * should start with or a RegExp to test against the field
19438      * @param {Boolean} anyMatch True to match any part not just the beginning
19439      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19440      */
19441     query : function(property, value, anyMatch){
19442         var fn = this.createFilterFn(property, value, anyMatch);
19443         return fn ? this.queryBy(fn) : this.data.clone();
19444     },
19445
19446     /**
19447      * Query by a function. The specified function will be called with each
19448      * record in this data source. If the function returns true the record is included
19449      * in the results.
19450      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19451      * @param {Object} scope (optional) The scope of the function (defaults to this)
19452       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19453      **/
19454     queryBy : function(fn, scope){
19455         var data = this.snapshot || this.data;
19456         return data.filterBy(fn, scope||this);
19457     },
19458
19459     /**
19460      * Collects unique values for a particular dataIndex from this store.
19461      * @param {String} dataIndex The property to collect
19462      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19463      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19464      * @return {Array} An array of the unique values
19465      **/
19466     collect : function(dataIndex, allowNull, bypassFilter){
19467         var d = (bypassFilter === true && this.snapshot) ?
19468                 this.snapshot.items : this.data.items;
19469         var v, sv, r = [], l = {};
19470         for(var i = 0, len = d.length; i < len; i++){
19471             v = d[i].data[dataIndex];
19472             sv = String(v);
19473             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19474                 l[sv] = true;
19475                 r[r.length] = v;
19476             }
19477         }
19478         return r;
19479     },
19480
19481     /**
19482      * Revert to a view of the Record cache with no filtering applied.
19483      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19484      */
19485     clearFilter : function(suppressEvent){
19486         if(this.snapshot && this.snapshot != this.data){
19487             this.data = this.snapshot;
19488             delete this.snapshot;
19489             if(suppressEvent !== true){
19490                 this.fireEvent("datachanged", this);
19491             }
19492         }
19493     },
19494
19495     // private
19496     afterEdit : function(record){
19497         if(this.modified.indexOf(record) == -1){
19498             this.modified.push(record);
19499         }
19500         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19501     },
19502
19503     // private
19504     afterReject : function(record){
19505         this.modified.remove(record);
19506         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19507     },
19508
19509     // private
19510     afterCommit : function(record){
19511         this.modified.remove(record);
19512         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19513     },
19514
19515     /**
19516      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19517      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19518      */
19519     commitChanges : function(){
19520         var m = this.modified.slice(0);
19521         this.modified = [];
19522         for(var i = 0, len = m.length; i < len; i++){
19523             m[i].commit();
19524         }
19525     },
19526
19527     /**
19528      * Cancel outstanding changes on all changed records.
19529      */
19530     rejectChanges : function(){
19531         var m = this.modified.slice(0);
19532         this.modified = [];
19533         for(var i = 0, len = m.length; i < len; i++){
19534             m[i].reject();
19535         }
19536     },
19537
19538     onMetaChange : function(meta, rtype, o){
19539         this.recordType = rtype;
19540         this.fields = rtype.prototype.fields;
19541         delete this.snapshot;
19542         this.sortInfo = meta.sortInfo;
19543         this.modified = [];
19544         this.fireEvent('metachange', this, this.reader.meta);
19545     }
19546 });/*
19547  * Based on:
19548  * Ext JS Library 1.1.1
19549  * Copyright(c) 2006-2007, Ext JS, LLC.
19550  *
19551  * Originally Released Under LGPL - original licence link has changed is not relivant.
19552  *
19553  * Fork - LGPL
19554  * <script type="text/javascript">
19555  */
19556
19557 /**
19558  * @class Roo.data.SimpleStore
19559  * @extends Roo.data.Store
19560  * Small helper class to make creating Stores from Array data easier.
19561  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19562  * @cfg {Array} fields An array of field definition objects, or field name strings.
19563  * @cfg {Array} data The multi-dimensional array of data
19564  * @constructor
19565  * @param {Object} config
19566  */
19567 Roo.data.SimpleStore = function(config){
19568     Roo.data.SimpleStore.superclass.constructor.call(this, {
19569         isLocal : true,
19570         reader: new Roo.data.ArrayReader({
19571                 id: config.id
19572             },
19573             Roo.data.Record.create(config.fields)
19574         ),
19575         proxy : new Roo.data.MemoryProxy(config.data)
19576     });
19577     this.load();
19578 };
19579 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19580  * Based on:
19581  * Ext JS Library 1.1.1
19582  * Copyright(c) 2006-2007, Ext JS, LLC.
19583  *
19584  * Originally Released Under LGPL - original licence link has changed is not relivant.
19585  *
19586  * Fork - LGPL
19587  * <script type="text/javascript">
19588  */
19589
19590 /**
19591 /**
19592  * @extends Roo.data.Store
19593  * @class Roo.data.JsonStore
19594  * Small helper class to make creating Stores for JSON data easier. <br/>
19595 <pre><code>
19596 var store = new Roo.data.JsonStore({
19597     url: 'get-images.php',
19598     root: 'images',
19599     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19600 });
19601 </code></pre>
19602  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19603  * JsonReader and HttpProxy (unless inline data is provided).</b>
19604  * @cfg {Array} fields An array of field definition objects, or field name strings.
19605  * @constructor
19606  * @param {Object} config
19607  */
19608 Roo.data.JsonStore = function(c){
19609     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19610         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19611         reader: new Roo.data.JsonReader(c, c.fields)
19612     }));
19613 };
19614 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19615  * Based on:
19616  * Ext JS Library 1.1.1
19617  * Copyright(c) 2006-2007, Ext JS, LLC.
19618  *
19619  * Originally Released Under LGPL - original licence link has changed is not relivant.
19620  *
19621  * Fork - LGPL
19622  * <script type="text/javascript">
19623  */
19624
19625  
19626 Roo.data.Field = function(config){
19627     if(typeof config == "string"){
19628         config = {name: config};
19629     }
19630     Roo.apply(this, config);
19631     
19632     if(!this.type){
19633         this.type = "auto";
19634     }
19635     
19636     var st = Roo.data.SortTypes;
19637     // named sortTypes are supported, here we look them up
19638     if(typeof this.sortType == "string"){
19639         this.sortType = st[this.sortType];
19640     }
19641     
19642     // set default sortType for strings and dates
19643     if(!this.sortType){
19644         switch(this.type){
19645             case "string":
19646                 this.sortType = st.asUCString;
19647                 break;
19648             case "date":
19649                 this.sortType = st.asDate;
19650                 break;
19651             default:
19652                 this.sortType = st.none;
19653         }
19654     }
19655
19656     // define once
19657     var stripRe = /[\$,%]/g;
19658
19659     // prebuilt conversion function for this field, instead of
19660     // switching every time we're reading a value
19661     if(!this.convert){
19662         var cv, dateFormat = this.dateFormat;
19663         switch(this.type){
19664             case "":
19665             case "auto":
19666             case undefined:
19667                 cv = function(v){ return v; };
19668                 break;
19669             case "string":
19670                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19671                 break;
19672             case "int":
19673                 cv = function(v){
19674                     return v !== undefined && v !== null && v !== '' ?
19675                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19676                     };
19677                 break;
19678             case "float":
19679                 cv = function(v){
19680                     return v !== undefined && v !== null && v !== '' ?
19681                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19682                     };
19683                 break;
19684             case "bool":
19685             case "boolean":
19686                 cv = function(v){ return v === true || v === "true" || v == 1; };
19687                 break;
19688             case "date":
19689                 cv = function(v){
19690                     if(!v){
19691                         return '';
19692                     }
19693                     if(v instanceof Date){
19694                         return v;
19695                     }
19696                     if(dateFormat){
19697                         if(dateFormat == "timestamp"){
19698                             return new Date(v*1000);
19699                         }
19700                         return Date.parseDate(v, dateFormat);
19701                     }
19702                     var parsed = Date.parse(v);
19703                     return parsed ? new Date(parsed) : null;
19704                 };
19705              break;
19706             
19707         }
19708         this.convert = cv;
19709     }
19710 };
19711
19712 Roo.data.Field.prototype = {
19713     dateFormat: null,
19714     defaultValue: "",
19715     mapping: null,
19716     sortType : null,
19717     sortDir : "ASC"
19718 };/*
19719  * Based on:
19720  * Ext JS Library 1.1.1
19721  * Copyright(c) 2006-2007, Ext JS, LLC.
19722  *
19723  * Originally Released Under LGPL - original licence link has changed is not relivant.
19724  *
19725  * Fork - LGPL
19726  * <script type="text/javascript">
19727  */
19728  
19729 // Base class for reading structured data from a data source.  This class is intended to be
19730 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19731
19732 /**
19733  * @class Roo.data.DataReader
19734  * Base class for reading structured data from a data source.  This class is intended to be
19735  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19736  */
19737
19738 Roo.data.DataReader = function(meta, recordType){
19739     
19740     this.meta = meta;
19741     
19742     this.recordType = recordType instanceof Array ? 
19743         Roo.data.Record.create(recordType) : recordType;
19744 };
19745
19746 Roo.data.DataReader.prototype = {
19747      /**
19748      * Create an empty record
19749      * @param {Object} data (optional) - overlay some values
19750      * @return {Roo.data.Record} record created.
19751      */
19752     newRow :  function(d) {
19753         var da =  {};
19754         this.recordType.prototype.fields.each(function(c) {
19755             switch( c.type) {
19756                 case 'int' : da[c.name] = 0; break;
19757                 case 'date' : da[c.name] = new Date(); break;
19758                 case 'float' : da[c.name] = 0.0; break;
19759                 case 'boolean' : da[c.name] = false; break;
19760                 default : da[c.name] = ""; break;
19761             }
19762             
19763         });
19764         return new this.recordType(Roo.apply(da, d));
19765     }
19766     
19767 };/*
19768  * Based on:
19769  * Ext JS Library 1.1.1
19770  * Copyright(c) 2006-2007, Ext JS, LLC.
19771  *
19772  * Originally Released Under LGPL - original licence link has changed is not relivant.
19773  *
19774  * Fork - LGPL
19775  * <script type="text/javascript">
19776  */
19777
19778 /**
19779  * @class Roo.data.DataProxy
19780  * @extends Roo.data.Observable
19781  * This class is an abstract base class for implementations which provide retrieval of
19782  * unformatted data objects.<br>
19783  * <p>
19784  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19785  * (of the appropriate type which knows how to parse the data object) to provide a block of
19786  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19787  * <p>
19788  * Custom implementations must implement the load method as described in
19789  * {@link Roo.data.HttpProxy#load}.
19790  */
19791 Roo.data.DataProxy = function(){
19792     this.addEvents({
19793         /**
19794          * @event beforeload
19795          * Fires before a network request is made to retrieve a data object.
19796          * @param {Object} This DataProxy object.
19797          * @param {Object} params The params parameter to the load function.
19798          */
19799         beforeload : true,
19800         /**
19801          * @event load
19802          * Fires before the load method's callback is called.
19803          * @param {Object} This DataProxy object.
19804          * @param {Object} o The data object.
19805          * @param {Object} arg The callback argument object passed to the load function.
19806          */
19807         load : true,
19808         /**
19809          * @event loadexception
19810          * Fires if an Exception occurs during data retrieval.
19811          * @param {Object} This DataProxy object.
19812          * @param {Object} o The data object.
19813          * @param {Object} arg The callback argument object passed to the load function.
19814          * @param {Object} e The Exception.
19815          */
19816         loadexception : true
19817     });
19818     Roo.data.DataProxy.superclass.constructor.call(this);
19819 };
19820
19821 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19822
19823     /**
19824      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19825      */
19826 /*
19827  * Based on:
19828  * Ext JS Library 1.1.1
19829  * Copyright(c) 2006-2007, Ext JS, LLC.
19830  *
19831  * Originally Released Under LGPL - original licence link has changed is not relivant.
19832  *
19833  * Fork - LGPL
19834  * <script type="text/javascript">
19835  */
19836 /**
19837  * @class Roo.data.MemoryProxy
19838  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19839  * to the Reader when its load method is called.
19840  * @constructor
19841  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19842  */
19843 Roo.data.MemoryProxy = function(data){
19844     if (data.data) {
19845         data = data.data;
19846     }
19847     Roo.data.MemoryProxy.superclass.constructor.call(this);
19848     this.data = data;
19849 };
19850
19851 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19852     /**
19853      * Load data from the requested source (in this case an in-memory
19854      * data object passed to the constructor), read the data object into
19855      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19856      * process that block using the passed callback.
19857      * @param {Object} params This parameter is not used by the MemoryProxy class.
19858      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19859      * object into a block of Roo.data.Records.
19860      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19861      * The function must be passed <ul>
19862      * <li>The Record block object</li>
19863      * <li>The "arg" argument from the load function</li>
19864      * <li>A boolean success indicator</li>
19865      * </ul>
19866      * @param {Object} scope The scope in which to call the callback
19867      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19868      */
19869     load : function(params, reader, callback, scope, arg){
19870         params = params || {};
19871         var result;
19872         try {
19873             result = reader.readRecords(this.data);
19874         }catch(e){
19875             this.fireEvent("loadexception", this, arg, null, e);
19876             callback.call(scope, null, arg, false);
19877             return;
19878         }
19879         callback.call(scope, result, arg, true);
19880     },
19881     
19882     // private
19883     update : function(params, records){
19884         
19885     }
19886 });/*
19887  * Based on:
19888  * Ext JS Library 1.1.1
19889  * Copyright(c) 2006-2007, Ext JS, LLC.
19890  *
19891  * Originally Released Under LGPL - original licence link has changed is not relivant.
19892  *
19893  * Fork - LGPL
19894  * <script type="text/javascript">
19895  */
19896 /**
19897  * @class Roo.data.HttpProxy
19898  * @extends Roo.data.DataProxy
19899  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19900  * configured to reference a certain URL.<br><br>
19901  * <p>
19902  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19903  * from which the running page was served.<br><br>
19904  * <p>
19905  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19906  * <p>
19907  * Be aware that to enable the browser to parse an XML document, the server must set
19908  * the Content-Type header in the HTTP response to "text/xml".
19909  * @constructor
19910  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19911  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19912  * will be used to make the request.
19913  */
19914 Roo.data.HttpProxy = function(conn){
19915     Roo.data.HttpProxy.superclass.constructor.call(this);
19916     // is conn a conn config or a real conn?
19917     this.conn = conn;
19918     this.useAjax = !conn || !conn.events;
19919   
19920 };
19921
19922 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19923     // thse are take from connection...
19924     
19925     /**
19926      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19927      */
19928     /**
19929      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19930      * extra parameters to each request made by this object. (defaults to undefined)
19931      */
19932     /**
19933      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19934      *  to each request made by this object. (defaults to undefined)
19935      */
19936     /**
19937      * @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)
19938      */
19939     /**
19940      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19941      */
19942      /**
19943      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19944      * @type Boolean
19945      */
19946   
19947
19948     /**
19949      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19950      * @type Boolean
19951      */
19952     /**
19953      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19954      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19955      * a finer-grained basis than the DataProxy events.
19956      */
19957     getConnection : function(){
19958         return this.useAjax ? Roo.Ajax : this.conn;
19959     },
19960
19961     /**
19962      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19963      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19964      * process that block using the passed callback.
19965      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19966      * for the request to the remote server.
19967      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19968      * object into a block of Roo.data.Records.
19969      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19970      * The function must be passed <ul>
19971      * <li>The Record block object</li>
19972      * <li>The "arg" argument from the load function</li>
19973      * <li>A boolean success indicator</li>
19974      * </ul>
19975      * @param {Object} scope The scope in which to call the callback
19976      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19977      */
19978     load : function(params, reader, callback, scope, arg){
19979         if(this.fireEvent("beforeload", this, params) !== false){
19980             var  o = {
19981                 params : params || {},
19982                 request: {
19983                     callback : callback,
19984                     scope : scope,
19985                     arg : arg
19986                 },
19987                 reader: reader,
19988                 callback : this.loadResponse,
19989                 scope: this
19990             };
19991             if(this.useAjax){
19992                 Roo.applyIf(o, this.conn);
19993                 if(this.activeRequest){
19994                     Roo.Ajax.abort(this.activeRequest);
19995                 }
19996                 this.activeRequest = Roo.Ajax.request(o);
19997             }else{
19998                 this.conn.request(o);
19999             }
20000         }else{
20001             callback.call(scope||this, null, arg, false);
20002         }
20003     },
20004
20005     // private
20006     loadResponse : function(o, success, response){
20007         delete this.activeRequest;
20008         if(!success){
20009             this.fireEvent("loadexception", this, o, response);
20010             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20011             return;
20012         }
20013         var result;
20014         try {
20015             result = o.reader.read(response);
20016         }catch(e){
20017             this.fireEvent("loadexception", this, o, response, e);
20018             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20019             return;
20020         }
20021         
20022         this.fireEvent("load", this, o, o.request.arg);
20023         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20024     },
20025
20026     // private
20027     update : function(dataSet){
20028
20029     },
20030
20031     // private
20032     updateResponse : function(dataSet){
20033
20034     }
20035 });/*
20036  * Based on:
20037  * Ext JS Library 1.1.1
20038  * Copyright(c) 2006-2007, Ext JS, LLC.
20039  *
20040  * Originally Released Under LGPL - original licence link has changed is not relivant.
20041  *
20042  * Fork - LGPL
20043  * <script type="text/javascript">
20044  */
20045
20046 /**
20047  * @class Roo.data.ScriptTagProxy
20048  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20049  * other than the originating domain of the running page.<br><br>
20050  * <p>
20051  * <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
20052  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20053  * <p>
20054  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20055  * source code that is used as the source inside a &lt;script> tag.<br><br>
20056  * <p>
20057  * In order for the browser to process the returned data, the server must wrap the data object
20058  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20059  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20060  * depending on whether the callback name was passed:
20061  * <p>
20062  * <pre><code>
20063 boolean scriptTag = false;
20064 String cb = request.getParameter("callback");
20065 if (cb != null) {
20066     scriptTag = true;
20067     response.setContentType("text/javascript");
20068 } else {
20069     response.setContentType("application/x-json");
20070 }
20071 Writer out = response.getWriter();
20072 if (scriptTag) {
20073     out.write(cb + "(");
20074 }
20075 out.print(dataBlock.toJsonString());
20076 if (scriptTag) {
20077     out.write(");");
20078 }
20079 </pre></code>
20080  *
20081  * @constructor
20082  * @param {Object} config A configuration object.
20083  */
20084 Roo.data.ScriptTagProxy = function(config){
20085     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20086     Roo.apply(this, config);
20087     this.head = document.getElementsByTagName("head")[0];
20088 };
20089
20090 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20091
20092 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20093     /**
20094      * @cfg {String} url The URL from which to request the data object.
20095      */
20096     /**
20097      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20098      */
20099     timeout : 30000,
20100     /**
20101      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20102      * the server the name of the callback function set up by the load call to process the returned data object.
20103      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20104      * javascript output which calls this named function passing the data object as its only parameter.
20105      */
20106     callbackParam : "callback",
20107     /**
20108      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20109      * name to the request.
20110      */
20111     nocache : true,
20112
20113     /**
20114      * Load data from the configured URL, read the data object into
20115      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20116      * process that block using the passed callback.
20117      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20118      * for the request to the remote server.
20119      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20120      * object into a block of Roo.data.Records.
20121      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20122      * The function must be passed <ul>
20123      * <li>The Record block object</li>
20124      * <li>The "arg" argument from the load function</li>
20125      * <li>A boolean success indicator</li>
20126      * </ul>
20127      * @param {Object} scope The scope in which to call the callback
20128      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20129      */
20130     load : function(params, reader, callback, scope, arg){
20131         if(this.fireEvent("beforeload", this, params) !== false){
20132
20133             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20134
20135             var url = this.url;
20136             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20137             if(this.nocache){
20138                 url += "&_dc=" + (new Date().getTime());
20139             }
20140             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20141             var trans = {
20142                 id : transId,
20143                 cb : "stcCallback"+transId,
20144                 scriptId : "stcScript"+transId,
20145                 params : params,
20146                 arg : arg,
20147                 url : url,
20148                 callback : callback,
20149                 scope : scope,
20150                 reader : reader
20151             };
20152             var conn = this;
20153
20154             window[trans.cb] = function(o){
20155                 conn.handleResponse(o, trans);
20156             };
20157
20158             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20159
20160             if(this.autoAbort !== false){
20161                 this.abort();
20162             }
20163
20164             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20165
20166             var script = document.createElement("script");
20167             script.setAttribute("src", url);
20168             script.setAttribute("type", "text/javascript");
20169             script.setAttribute("id", trans.scriptId);
20170             this.head.appendChild(script);
20171
20172             this.trans = trans;
20173         }else{
20174             callback.call(scope||this, null, arg, false);
20175         }
20176     },
20177
20178     // private
20179     isLoading : function(){
20180         return this.trans ? true : false;
20181     },
20182
20183     /**
20184      * Abort the current server request.
20185      */
20186     abort : function(){
20187         if(this.isLoading()){
20188             this.destroyTrans(this.trans);
20189         }
20190     },
20191
20192     // private
20193     destroyTrans : function(trans, isLoaded){
20194         this.head.removeChild(document.getElementById(trans.scriptId));
20195         clearTimeout(trans.timeoutId);
20196         if(isLoaded){
20197             window[trans.cb] = undefined;
20198             try{
20199                 delete window[trans.cb];
20200             }catch(e){}
20201         }else{
20202             // if hasn't been loaded, wait for load to remove it to prevent script error
20203             window[trans.cb] = function(){
20204                 window[trans.cb] = undefined;
20205                 try{
20206                     delete window[trans.cb];
20207                 }catch(e){}
20208             };
20209         }
20210     },
20211
20212     // private
20213     handleResponse : function(o, trans){
20214         this.trans = false;
20215         this.destroyTrans(trans, true);
20216         var result;
20217         try {
20218             result = trans.reader.readRecords(o);
20219         }catch(e){
20220             this.fireEvent("loadexception", this, o, trans.arg, e);
20221             trans.callback.call(trans.scope||window, null, trans.arg, false);
20222             return;
20223         }
20224         this.fireEvent("load", this, o, trans.arg);
20225         trans.callback.call(trans.scope||window, result, trans.arg, true);
20226     },
20227
20228     // private
20229     handleFailure : function(trans){
20230         this.trans = false;
20231         this.destroyTrans(trans, false);
20232         this.fireEvent("loadexception", this, null, trans.arg);
20233         trans.callback.call(trans.scope||window, null, trans.arg, false);
20234     }
20235 });/*
20236  * Based on:
20237  * Ext JS Library 1.1.1
20238  * Copyright(c) 2006-2007, Ext JS, LLC.
20239  *
20240  * Originally Released Under LGPL - original licence link has changed is not relivant.
20241  *
20242  * Fork - LGPL
20243  * <script type="text/javascript">
20244  */
20245
20246 /**
20247  * @class Roo.data.JsonReader
20248  * @extends Roo.data.DataReader
20249  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20250  * based on mappings in a provided Roo.data.Record constructor.
20251  * <p>
20252  * Example code:
20253  * <pre><code>
20254 var RecordDef = Roo.data.Record.create([
20255     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20256     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20257 ]);
20258 var myReader = new Roo.data.JsonReader({
20259     totalProperty: "results",    // The property which contains the total dataset size (optional)
20260     root: "rows",                // The property which contains an Array of row objects
20261     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20262 }, RecordDef);
20263 </code></pre>
20264  * <p>
20265  * This would consume a JSON file like this:
20266  * <pre><code>
20267 { 'results': 2, 'rows': [
20268     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20269     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20270 }
20271 </code></pre>
20272  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20273  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20274  * paged from the remote server.
20275  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20276  * @cfg {String} root name of the property which contains the Array of row objects.
20277  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20278  * @constructor
20279  * Create a new JsonReader
20280  * @param {Object} meta Metadata configuration options
20281  * @param {Object} recordType Either an Array of field definition objects,
20282  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20283  */
20284 Roo.data.JsonReader = function(meta, recordType){
20285     
20286     meta = meta || {};
20287     // set some defaults:
20288     Roo.applyIf(meta, {
20289         totalProperty: 'total',
20290         successProperty : 'success',
20291         root : 'data',
20292         id : 'id'
20293     });
20294     
20295     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20296 };
20297 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20298     /**
20299      * This method is only used by a DataProxy which has retrieved data from a remote server.
20300      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20301      * @return {Object} data A data block which is used by an Roo.data.Store object as
20302      * a cache of Roo.data.Records.
20303      */
20304     read : function(response){
20305         var json = response.responseText;
20306         /* eval:var:o */
20307         var o = eval("("+json+")");
20308         if(!o) {
20309             throw {message: "JsonReader.read: Json object not found"};
20310         }
20311         
20312         if(o.metaData){
20313             delete this.ef;
20314             this.meta = o.metaData;
20315             this.recordType = Roo.data.Record.create(o.metaData.fields);
20316             this.onMetaChange(this.meta, this.recordType, o);
20317         }
20318         return this.readRecords(o);
20319     },
20320
20321     // private function a store will implement
20322     onMetaChange : function(meta, recordType, o){
20323
20324     },
20325
20326     /**
20327          * @ignore
20328          */
20329     simpleAccess: function(obj, subsc) {
20330         return obj[subsc];
20331     },
20332
20333         /**
20334          * @ignore
20335          */
20336     getJsonAccessor: function(){
20337         var re = /[\[\.]/;
20338         return function(expr) {
20339             try {
20340                 return(re.test(expr))
20341                     ? new Function("obj", "return obj." + expr)
20342                     : function(obj){
20343                         return obj[expr];
20344                     };
20345             } catch(e){}
20346             return Roo.emptyFn;
20347         };
20348     }(),
20349
20350     /**
20351      * Create a data block containing Roo.data.Records from an XML document.
20352      * @param {Object} o An object which contains an Array of row objects in the property specified
20353      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20354      * which contains the total size of the dataset.
20355      * @return {Object} data A data block which is used by an Roo.data.Store object as
20356      * a cache of Roo.data.Records.
20357      */
20358     readRecords : function(o){
20359         /**
20360          * After any data loads, the raw JSON data is available for further custom processing.
20361          * @type Object
20362          */
20363         this.jsonData = o;
20364         var s = this.meta, Record = this.recordType,
20365             f = Record.prototype.fields, fi = f.items, fl = f.length;
20366
20367 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20368         if (!this.ef) {
20369             if(s.totalProperty) {
20370                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20371                 }
20372                 if(s.successProperty) {
20373                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20374                 }
20375                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20376                 if (s.id) {
20377                         var g = this.getJsonAccessor(s.id);
20378                         this.getId = function(rec) {
20379                                 var r = g(rec);
20380                                 return (r === undefined || r === "") ? null : r;
20381                         };
20382                 } else {
20383                         this.getId = function(){return null;};
20384                 }
20385             this.ef = [];
20386             for(var i = 0; i < fl; i++){
20387                 f = fi[i];
20388                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20389                 this.ef[i] = this.getJsonAccessor(map);
20390             }
20391         }
20392
20393         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20394         if(s.totalProperty){
20395             var v = parseInt(this.getTotal(o), 10);
20396             if(!isNaN(v)){
20397                 totalRecords = v;
20398             }
20399         }
20400         if(s.successProperty){
20401             var v = this.getSuccess(o);
20402             if(v === false || v === 'false'){
20403                 success = false;
20404             }
20405         }
20406         var records = [];
20407             for(var i = 0; i < c; i++){
20408                     var n = root[i];
20409                 var values = {};
20410                 var id = this.getId(n);
20411                 for(var j = 0; j < fl; j++){
20412                     f = fi[j];
20413                 var v = this.ef[j](n);
20414                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20415                 }
20416                 var record = new Record(values, id);
20417                 record.json = n;
20418                 records[i] = record;
20419             }
20420             return {
20421                 success : success,
20422                 records : records,
20423                 totalRecords : totalRecords
20424             };
20425     }
20426 });/*
20427  * Based on:
20428  * Ext JS Library 1.1.1
20429  * Copyright(c) 2006-2007, Ext JS, LLC.
20430  *
20431  * Originally Released Under LGPL - original licence link has changed is not relivant.
20432  *
20433  * Fork - LGPL
20434  * <script type="text/javascript">
20435  */
20436
20437 /**
20438  * @class Roo.data.XmlReader
20439  * @extends Roo.data.DataReader
20440  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20441  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20442  * <p>
20443  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20444  * header in the HTTP response must be set to "text/xml".</em>
20445  * <p>
20446  * Example code:
20447  * <pre><code>
20448 var RecordDef = Roo.data.Record.create([
20449    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20450    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20451 ]);
20452 var myReader = new Roo.data.XmlReader({
20453    totalRecords: "results", // The element which contains the total dataset size (optional)
20454    record: "row",           // The repeated element which contains row information
20455    id: "id"                 // The element within the row that provides an ID for the record (optional)
20456 }, RecordDef);
20457 </code></pre>
20458  * <p>
20459  * This would consume an XML file like this:
20460  * <pre><code>
20461 &lt;?xml?>
20462 &lt;dataset>
20463  &lt;results>2&lt;/results>
20464  &lt;row>
20465    &lt;id>1&lt;/id>
20466    &lt;name>Bill&lt;/name>
20467    &lt;occupation>Gardener&lt;/occupation>
20468  &lt;/row>
20469  &lt;row>
20470    &lt;id>2&lt;/id>
20471    &lt;name>Ben&lt;/name>
20472    &lt;occupation>Horticulturalist&lt;/occupation>
20473  &lt;/row>
20474 &lt;/dataset>
20475 </code></pre>
20476  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20477  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20478  * paged from the remote server.
20479  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20480  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20481  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20482  * a record identifier value.
20483  * @constructor
20484  * Create a new XmlReader
20485  * @param {Object} meta Metadata configuration options
20486  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20487  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20488  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20489  */
20490 Roo.data.XmlReader = function(meta, recordType){
20491     meta = meta || {};
20492     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20493 };
20494 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20495     /**
20496      * This method is only used by a DataProxy which has retrieved data from a remote server.
20497          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20498          * to contain a method called 'responseXML' that returns an XML document object.
20499      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20500      * a cache of Roo.data.Records.
20501      */
20502     read : function(response){
20503         var doc = response.responseXML;
20504         if(!doc) {
20505             throw {message: "XmlReader.read: XML Document not available"};
20506         }
20507         return this.readRecords(doc);
20508     },
20509
20510     /**
20511      * Create a data block containing Roo.data.Records from an XML document.
20512          * @param {Object} doc A parsed XML document.
20513      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20514      * a cache of Roo.data.Records.
20515      */
20516     readRecords : function(doc){
20517         /**
20518          * After any data loads/reads, the raw XML Document is available for further custom processing.
20519          * @type XMLDocument
20520          */
20521         this.xmlData = doc;
20522         var root = doc.documentElement || doc;
20523         var q = Roo.DomQuery;
20524         var recordType = this.recordType, fields = recordType.prototype.fields;
20525         var sid = this.meta.id;
20526         var totalRecords = 0, success = true;
20527         if(this.meta.totalRecords){
20528             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20529         }
20530         
20531         if(this.meta.success){
20532             var sv = q.selectValue(this.meta.success, root, true);
20533             success = sv !== false && sv !== 'false';
20534         }
20535         var records = [];
20536         var ns = q.select(this.meta.record, root);
20537         for(var i = 0, len = ns.length; i < len; i++) {
20538                 var n = ns[i];
20539                 var values = {};
20540                 var id = sid ? q.selectValue(sid, n) : undefined;
20541                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20542                     var f = fields.items[j];
20543                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20544                     v = f.convert(v);
20545                     values[f.name] = v;
20546                 }
20547                 var record = new recordType(values, id);
20548                 record.node = n;
20549                 records[records.length] = record;
20550             }
20551
20552             return {
20553                 success : success,
20554                 records : records,
20555                 totalRecords : totalRecords || records.length
20556             };
20557     }
20558 });/*
20559  * Based on:
20560  * Ext JS Library 1.1.1
20561  * Copyright(c) 2006-2007, Ext JS, LLC.
20562  *
20563  * Originally Released Under LGPL - original licence link has changed is not relivant.
20564  *
20565  * Fork - LGPL
20566  * <script type="text/javascript">
20567  */
20568
20569 /**
20570  * @class Roo.data.ArrayReader
20571  * @extends Roo.data.DataReader
20572  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20573  * Each element of that Array represents a row of data fields. The
20574  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20575  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20576  * <p>
20577  * Example code:.
20578  * <pre><code>
20579 var RecordDef = Roo.data.Record.create([
20580     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20581     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20582 ]);
20583 var myReader = new Roo.data.ArrayReader({
20584     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20585 }, RecordDef);
20586 </code></pre>
20587  * <p>
20588  * This would consume an Array like this:
20589  * <pre><code>
20590 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20591   </code></pre>
20592  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20593  * @constructor
20594  * Create a new JsonReader
20595  * @param {Object} meta Metadata configuration options.
20596  * @param {Object} recordType Either an Array of field definition objects
20597  * as specified to {@link Roo.data.Record#create},
20598  * or an {@link Roo.data.Record} object
20599  * created using {@link Roo.data.Record#create}.
20600  */
20601 Roo.data.ArrayReader = function(meta, recordType){
20602     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20603 };
20604
20605 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20606     /**
20607      * Create a data block containing Roo.data.Records from an XML document.
20608      * @param {Object} o An Array of row objects which represents the dataset.
20609      * @return {Object} data A data block which is used by an Roo.data.Store object as
20610      * a cache of Roo.data.Records.
20611      */
20612     readRecords : function(o){
20613         var sid = this.meta ? this.meta.id : null;
20614         var recordType = this.recordType, fields = recordType.prototype.fields;
20615         var records = [];
20616         var root = o;
20617             for(var i = 0; i < root.length; i++){
20618                     var n = root[i];
20619                 var values = {};
20620                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20621                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20622                 var f = fields.items[j];
20623                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20624                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20625                 v = f.convert(v);
20626                 values[f.name] = v;
20627             }
20628                 var record = new recordType(values, id);
20629                 record.json = n;
20630                 records[records.length] = record;
20631             }
20632             return {
20633                 records : records,
20634                 totalRecords : records.length
20635             };
20636     }
20637 });/*
20638  * Based on:
20639  * Ext JS Library 1.1.1
20640  * Copyright(c) 2006-2007, Ext JS, LLC.
20641  *
20642  * Originally Released Under LGPL - original licence link has changed is not relivant.
20643  *
20644  * Fork - LGPL
20645  * <script type="text/javascript">
20646  */
20647
20648
20649 /**
20650  * @class Roo.data.Tree
20651  * @extends Roo.util.Observable
20652  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20653  * in the tree have most standard DOM functionality.
20654  * @constructor
20655  * @param {Node} root (optional) The root node
20656  */
20657 Roo.data.Tree = function(root){
20658    this.nodeHash = {};
20659    /**
20660     * The root node for this tree
20661     * @type Node
20662     */
20663    this.root = null;
20664    if(root){
20665        this.setRootNode(root);
20666    }
20667    this.addEvents({
20668        /**
20669         * @event append
20670         * Fires when a new child node is appended to a node in this tree.
20671         * @param {Tree} tree The owner tree
20672         * @param {Node} parent The parent node
20673         * @param {Node} node The newly appended node
20674         * @param {Number} index The index of the newly appended node
20675         */
20676        "append" : true,
20677        /**
20678         * @event remove
20679         * Fires when a child node is removed from a node in this tree.
20680         * @param {Tree} tree The owner tree
20681         * @param {Node} parent The parent node
20682         * @param {Node} node The child node removed
20683         */
20684        "remove" : true,
20685        /**
20686         * @event move
20687         * Fires when a node is moved to a new location in the tree
20688         * @param {Tree} tree The owner tree
20689         * @param {Node} node The node moved
20690         * @param {Node} oldParent The old parent of this node
20691         * @param {Node} newParent The new parent of this node
20692         * @param {Number} index The index it was moved to
20693         */
20694        "move" : true,
20695        /**
20696         * @event insert
20697         * Fires when a new child node is inserted in a node in this tree.
20698         * @param {Tree} tree The owner tree
20699         * @param {Node} parent The parent node
20700         * @param {Node} node The child node inserted
20701         * @param {Node} refNode The child node the node was inserted before
20702         */
20703        "insert" : true,
20704        /**
20705         * @event beforeappend
20706         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20707         * @param {Tree} tree The owner tree
20708         * @param {Node} parent The parent node
20709         * @param {Node} node The child node to be appended
20710         */
20711        "beforeappend" : true,
20712        /**
20713         * @event beforeremove
20714         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20715         * @param {Tree} tree The owner tree
20716         * @param {Node} parent The parent node
20717         * @param {Node} node The child node to be removed
20718         */
20719        "beforeremove" : true,
20720        /**
20721         * @event beforemove
20722         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20723         * @param {Tree} tree The owner tree
20724         * @param {Node} node The node being moved
20725         * @param {Node} oldParent The parent of the node
20726         * @param {Node} newParent The new parent the node is moving to
20727         * @param {Number} index The index it is being moved to
20728         */
20729        "beforemove" : true,
20730        /**
20731         * @event beforeinsert
20732         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20733         * @param {Tree} tree The owner tree
20734         * @param {Node} parent The parent node
20735         * @param {Node} node The child node to be inserted
20736         * @param {Node} refNode The child node the node is being inserted before
20737         */
20738        "beforeinsert" : true
20739    });
20740
20741     Roo.data.Tree.superclass.constructor.call(this);
20742 };
20743
20744 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20745     pathSeparator: "/",
20746
20747     proxyNodeEvent : function(){
20748         return this.fireEvent.apply(this, arguments);
20749     },
20750
20751     /**
20752      * Returns the root node for this tree.
20753      * @return {Node}
20754      */
20755     getRootNode : function(){
20756         return this.root;
20757     },
20758
20759     /**
20760      * Sets the root node for this tree.
20761      * @param {Node} node
20762      * @return {Node}
20763      */
20764     setRootNode : function(node){
20765         this.root = node;
20766         node.ownerTree = this;
20767         node.isRoot = true;
20768         this.registerNode(node);
20769         return node;
20770     },
20771
20772     /**
20773      * Gets a node in this tree by its id.
20774      * @param {String} id
20775      * @return {Node}
20776      */
20777     getNodeById : function(id){
20778         return this.nodeHash[id];
20779     },
20780
20781     registerNode : function(node){
20782         this.nodeHash[node.id] = node;
20783     },
20784
20785     unregisterNode : function(node){
20786         delete this.nodeHash[node.id];
20787     },
20788
20789     toString : function(){
20790         return "[Tree"+(this.id?" "+this.id:"")+"]";
20791     }
20792 });
20793
20794 /**
20795  * @class Roo.data.Node
20796  * @extends Roo.util.Observable
20797  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20798  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20799  * @constructor
20800  * @param {Object} attributes The attributes/config for the node
20801  */
20802 Roo.data.Node = function(attributes){
20803     /**
20804      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20805      * @type {Object}
20806      */
20807     this.attributes = attributes || {};
20808     this.leaf = this.attributes.leaf;
20809     /**
20810      * The node id. @type String
20811      */
20812     this.id = this.attributes.id;
20813     if(!this.id){
20814         this.id = Roo.id(null, "ynode-");
20815         this.attributes.id = this.id;
20816     }
20817     /**
20818      * All child nodes of this node. @type Array
20819      */
20820     this.childNodes = [];
20821     if(!this.childNodes.indexOf){ // indexOf is a must
20822         this.childNodes.indexOf = function(o){
20823             for(var i = 0, len = this.length; i < len; i++){
20824                 if(this[i] == o) return i;
20825             }
20826             return -1;
20827         };
20828     }
20829     /**
20830      * The parent node for this node. @type Node
20831      */
20832     this.parentNode = null;
20833     /**
20834      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20835      */
20836     this.firstChild = null;
20837     /**
20838      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20839      */
20840     this.lastChild = null;
20841     /**
20842      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20843      */
20844     this.previousSibling = null;
20845     /**
20846      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20847      */
20848     this.nextSibling = null;
20849
20850     this.addEvents({
20851        /**
20852         * @event append
20853         * Fires when a new child node is appended
20854         * @param {Tree} tree The owner tree
20855         * @param {Node} this This node
20856         * @param {Node} node The newly appended node
20857         * @param {Number} index The index of the newly appended node
20858         */
20859        "append" : true,
20860        /**
20861         * @event remove
20862         * Fires when a child node is removed
20863         * @param {Tree} tree The owner tree
20864         * @param {Node} this This node
20865         * @param {Node} node The removed node
20866         */
20867        "remove" : true,
20868        /**
20869         * @event move
20870         * Fires when this node is moved to a new location in the tree
20871         * @param {Tree} tree The owner tree
20872         * @param {Node} this This node
20873         * @param {Node} oldParent The old parent of this node
20874         * @param {Node} newParent The new parent of this node
20875         * @param {Number} index The index it was moved to
20876         */
20877        "move" : true,
20878        /**
20879         * @event insert
20880         * Fires when a new child node is inserted.
20881         * @param {Tree} tree The owner tree
20882         * @param {Node} this This node
20883         * @param {Node} node The child node inserted
20884         * @param {Node} refNode The child node the node was inserted before
20885         */
20886        "insert" : true,
20887        /**
20888         * @event beforeappend
20889         * Fires before a new child is appended, return false to cancel the append.
20890         * @param {Tree} tree The owner tree
20891         * @param {Node} this This node
20892         * @param {Node} node The child node to be appended
20893         */
20894        "beforeappend" : true,
20895        /**
20896         * @event beforeremove
20897         * Fires before a child is removed, return false to cancel the remove.
20898         * @param {Tree} tree The owner tree
20899         * @param {Node} this This node
20900         * @param {Node} node The child node to be removed
20901         */
20902        "beforeremove" : true,
20903        /**
20904         * @event beforemove
20905         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20906         * @param {Tree} tree The owner tree
20907         * @param {Node} this This node
20908         * @param {Node} oldParent The parent of this node
20909         * @param {Node} newParent The new parent this node is moving to
20910         * @param {Number} index The index it is being moved to
20911         */
20912        "beforemove" : true,
20913        /**
20914         * @event beforeinsert
20915         * Fires before a new child is inserted, return false to cancel the insert.
20916         * @param {Tree} tree The owner tree
20917         * @param {Node} this This node
20918         * @param {Node} node The child node to be inserted
20919         * @param {Node} refNode The child node the node is being inserted before
20920         */
20921        "beforeinsert" : true
20922    });
20923     this.listeners = this.attributes.listeners;
20924     Roo.data.Node.superclass.constructor.call(this);
20925 };
20926
20927 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20928     fireEvent : function(evtName){
20929         // first do standard event for this node
20930         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20931             return false;
20932         }
20933         // then bubble it up to the tree if the event wasn't cancelled
20934         var ot = this.getOwnerTree();
20935         if(ot){
20936             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20937                 return false;
20938             }
20939         }
20940         return true;
20941     },
20942
20943     /**
20944      * Returns true if this node is a leaf
20945      * @return {Boolean}
20946      */
20947     isLeaf : function(){
20948         return this.leaf === true;
20949     },
20950
20951     // private
20952     setFirstChild : function(node){
20953         this.firstChild = node;
20954     },
20955
20956     //private
20957     setLastChild : function(node){
20958         this.lastChild = node;
20959     },
20960
20961
20962     /**
20963      * Returns true if this node is the last child of its parent
20964      * @return {Boolean}
20965      */
20966     isLast : function(){
20967        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20968     },
20969
20970     /**
20971      * Returns true if this node is the first child of its parent
20972      * @return {Boolean}
20973      */
20974     isFirst : function(){
20975        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20976     },
20977
20978     hasChildNodes : function(){
20979         return !this.isLeaf() && this.childNodes.length > 0;
20980     },
20981
20982     /**
20983      * Insert node(s) as the last child node of this node.
20984      * @param {Node/Array} node The node or Array of nodes to append
20985      * @return {Node} The appended node if single append, or null if an array was passed
20986      */
20987     appendChild : function(node){
20988         var multi = false;
20989         if(node instanceof Array){
20990             multi = node;
20991         }else if(arguments.length > 1){
20992             multi = arguments;
20993         }
20994         // if passed an array or multiple args do them one by one
20995         if(multi){
20996             for(var i = 0, len = multi.length; i < len; i++) {
20997                 this.appendChild(multi[i]);
20998             }
20999         }else{
21000             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21001                 return false;
21002             }
21003             var index = this.childNodes.length;
21004             var oldParent = node.parentNode;
21005             // it's a move, make sure we move it cleanly
21006             if(oldParent){
21007                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21008                     return false;
21009                 }
21010                 oldParent.removeChild(node);
21011             }
21012             index = this.childNodes.length;
21013             if(index == 0){
21014                 this.setFirstChild(node);
21015             }
21016             this.childNodes.push(node);
21017             node.parentNode = this;
21018             var ps = this.childNodes[index-1];
21019             if(ps){
21020                 node.previousSibling = ps;
21021                 ps.nextSibling = node;
21022             }else{
21023                 node.previousSibling = null;
21024             }
21025             node.nextSibling = null;
21026             this.setLastChild(node);
21027             node.setOwnerTree(this.getOwnerTree());
21028             this.fireEvent("append", this.ownerTree, this, node, index);
21029             if(oldParent){
21030                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21031             }
21032             return node;
21033         }
21034     },
21035
21036     /**
21037      * Removes a child node from this node.
21038      * @param {Node} node The node to remove
21039      * @return {Node} The removed node
21040      */
21041     removeChild : function(node){
21042         var index = this.childNodes.indexOf(node);
21043         if(index == -1){
21044             return false;
21045         }
21046         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21047             return false;
21048         }
21049
21050         // remove it from childNodes collection
21051         this.childNodes.splice(index, 1);
21052
21053         // update siblings
21054         if(node.previousSibling){
21055             node.previousSibling.nextSibling = node.nextSibling;
21056         }
21057         if(node.nextSibling){
21058             node.nextSibling.previousSibling = node.previousSibling;
21059         }
21060
21061         // update child refs
21062         if(this.firstChild == node){
21063             this.setFirstChild(node.nextSibling);
21064         }
21065         if(this.lastChild == node){
21066             this.setLastChild(node.previousSibling);
21067         }
21068
21069         node.setOwnerTree(null);
21070         // clear any references from the node
21071         node.parentNode = null;
21072         node.previousSibling = null;
21073         node.nextSibling = null;
21074         this.fireEvent("remove", this.ownerTree, this, node);
21075         return node;
21076     },
21077
21078     /**
21079      * Inserts the first node before the second node in this nodes childNodes collection.
21080      * @param {Node} node The node to insert
21081      * @param {Node} refNode The node to insert before (if null the node is appended)
21082      * @return {Node} The inserted node
21083      */
21084     insertBefore : function(node, refNode){
21085         if(!refNode){ // like standard Dom, refNode can be null for append
21086             return this.appendChild(node);
21087         }
21088         // nothing to do
21089         if(node == refNode){
21090             return false;
21091         }
21092
21093         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21094             return false;
21095         }
21096         var index = this.childNodes.indexOf(refNode);
21097         var oldParent = node.parentNode;
21098         var refIndex = index;
21099
21100         // when moving internally, indexes will change after remove
21101         if(oldParent == this && this.childNodes.indexOf(node) < index){
21102             refIndex--;
21103         }
21104
21105         // it's a move, make sure we move it cleanly
21106         if(oldParent){
21107             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21108                 return false;
21109             }
21110             oldParent.removeChild(node);
21111         }
21112         if(refIndex == 0){
21113             this.setFirstChild(node);
21114         }
21115         this.childNodes.splice(refIndex, 0, node);
21116         node.parentNode = this;
21117         var ps = this.childNodes[refIndex-1];
21118         if(ps){
21119             node.previousSibling = ps;
21120             ps.nextSibling = node;
21121         }else{
21122             node.previousSibling = null;
21123         }
21124         node.nextSibling = refNode;
21125         refNode.previousSibling = node;
21126         node.setOwnerTree(this.getOwnerTree());
21127         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21128         if(oldParent){
21129             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21130         }
21131         return node;
21132     },
21133
21134     /**
21135      * Returns the child node at the specified index.
21136      * @param {Number} index
21137      * @return {Node}
21138      */
21139     item : function(index){
21140         return this.childNodes[index];
21141     },
21142
21143     /**
21144      * Replaces one child node in this node with another.
21145      * @param {Node} newChild The replacement node
21146      * @param {Node} oldChild The node to replace
21147      * @return {Node} The replaced node
21148      */
21149     replaceChild : function(newChild, oldChild){
21150         this.insertBefore(newChild, oldChild);
21151         this.removeChild(oldChild);
21152         return oldChild;
21153     },
21154
21155     /**
21156      * Returns the index of a child node
21157      * @param {Node} node
21158      * @return {Number} The index of the node or -1 if it was not found
21159      */
21160     indexOf : function(child){
21161         return this.childNodes.indexOf(child);
21162     },
21163
21164     /**
21165      * Returns the tree this node is in.
21166      * @return {Tree}
21167      */
21168     getOwnerTree : function(){
21169         // if it doesn't have one, look for one
21170         if(!this.ownerTree){
21171             var p = this;
21172             while(p){
21173                 if(p.ownerTree){
21174                     this.ownerTree = p.ownerTree;
21175                     break;
21176                 }
21177                 p = p.parentNode;
21178             }
21179         }
21180         return this.ownerTree;
21181     },
21182
21183     /**
21184      * Returns depth of this node (the root node has a depth of 0)
21185      * @return {Number}
21186      */
21187     getDepth : function(){
21188         var depth = 0;
21189         var p = this;
21190         while(p.parentNode){
21191             ++depth;
21192             p = p.parentNode;
21193         }
21194         return depth;
21195     },
21196
21197     // private
21198     setOwnerTree : function(tree){
21199         // if it's move, we need to update everyone
21200         if(tree != this.ownerTree){
21201             if(this.ownerTree){
21202                 this.ownerTree.unregisterNode(this);
21203             }
21204             this.ownerTree = tree;
21205             var cs = this.childNodes;
21206             for(var i = 0, len = cs.length; i < len; i++) {
21207                 cs[i].setOwnerTree(tree);
21208             }
21209             if(tree){
21210                 tree.registerNode(this);
21211             }
21212         }
21213     },
21214
21215     /**
21216      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21217      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21218      * @return {String} The path
21219      */
21220     getPath : function(attr){
21221         attr = attr || "id";
21222         var p = this.parentNode;
21223         var b = [this.attributes[attr]];
21224         while(p){
21225             b.unshift(p.attributes[attr]);
21226             p = p.parentNode;
21227         }
21228         var sep = this.getOwnerTree().pathSeparator;
21229         return sep + b.join(sep);
21230     },
21231
21232     /**
21233      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21234      * function call will be the scope provided or the current node. The arguments to the function
21235      * will be the args provided or the current node. If the function returns false at any point,
21236      * the bubble is stopped.
21237      * @param {Function} fn The function to call
21238      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21239      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21240      */
21241     bubble : function(fn, scope, args){
21242         var p = this;
21243         while(p){
21244             if(fn.call(scope || p, args || p) === false){
21245                 break;
21246             }
21247             p = p.parentNode;
21248         }
21249     },
21250
21251     /**
21252      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21253      * function call will be the scope provided or the current node. The arguments to the function
21254      * will be the args provided or the current node. If the function returns false at any point,
21255      * the cascade is stopped on that branch.
21256      * @param {Function} fn The function to call
21257      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21258      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21259      */
21260     cascade : function(fn, scope, args){
21261         if(fn.call(scope || this, args || this) !== false){
21262             var cs = this.childNodes;
21263             for(var i = 0, len = cs.length; i < len; i++) {
21264                 cs[i].cascade(fn, scope, args);
21265             }
21266         }
21267     },
21268
21269     /**
21270      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21271      * function call will be the scope provided or the current node. The arguments to the function
21272      * will be the args provided or the current node. If the function returns false at any point,
21273      * the iteration stops.
21274      * @param {Function} fn The function to call
21275      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21276      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21277      */
21278     eachChild : function(fn, scope, args){
21279         var cs = this.childNodes;
21280         for(var i = 0, len = cs.length; i < len; i++) {
21281                 if(fn.call(scope || this, args || cs[i]) === false){
21282                     break;
21283                 }
21284         }
21285     },
21286
21287     /**
21288      * Finds the first child that has the attribute with the specified value.
21289      * @param {String} attribute The attribute name
21290      * @param {Mixed} value The value to search for
21291      * @return {Node} The found child or null if none was found
21292      */
21293     findChild : function(attribute, value){
21294         var cs = this.childNodes;
21295         for(var i = 0, len = cs.length; i < len; i++) {
21296                 if(cs[i].attributes[attribute] == value){
21297                     return cs[i];
21298                 }
21299         }
21300         return null;
21301     },
21302
21303     /**
21304      * Finds the first child by a custom function. The child matches if the function passed
21305      * returns true.
21306      * @param {Function} fn
21307      * @param {Object} scope (optional)
21308      * @return {Node} The found child or null if none was found
21309      */
21310     findChildBy : function(fn, scope){
21311         var cs = this.childNodes;
21312         for(var i = 0, len = cs.length; i < len; i++) {
21313                 if(fn.call(scope||cs[i], cs[i]) === true){
21314                     return cs[i];
21315                 }
21316         }
21317         return null;
21318     },
21319
21320     /**
21321      * Sorts this nodes children using the supplied sort function
21322      * @param {Function} fn
21323      * @param {Object} scope (optional)
21324      */
21325     sort : function(fn, scope){
21326         var cs = this.childNodes;
21327         var len = cs.length;
21328         if(len > 0){
21329             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21330             cs.sort(sortFn);
21331             for(var i = 0; i < len; i++){
21332                 var n = cs[i];
21333                 n.previousSibling = cs[i-1];
21334                 n.nextSibling = cs[i+1];
21335                 if(i == 0){
21336                     this.setFirstChild(n);
21337                 }
21338                 if(i == len-1){
21339                     this.setLastChild(n);
21340                 }
21341             }
21342         }
21343     },
21344
21345     /**
21346      * Returns true if this node is an ancestor (at any point) of the passed node.
21347      * @param {Node} node
21348      * @return {Boolean}
21349      */
21350     contains : function(node){
21351         return node.isAncestor(this);
21352     },
21353
21354     /**
21355      * Returns true if the passed node is an ancestor (at any point) of this node.
21356      * @param {Node} node
21357      * @return {Boolean}
21358      */
21359     isAncestor : function(node){
21360         var p = this.parentNode;
21361         while(p){
21362             if(p == node){
21363                 return true;
21364             }
21365             p = p.parentNode;
21366         }
21367         return false;
21368     },
21369
21370     toString : function(){
21371         return "[Node"+(this.id?" "+this.id:"")+"]";
21372     }
21373 });/*
21374  * Based on:
21375  * Ext JS Library 1.1.1
21376  * Copyright(c) 2006-2007, Ext JS, LLC.
21377  *
21378  * Originally Released Under LGPL - original licence link has changed is not relivant.
21379  *
21380  * Fork - LGPL
21381  * <script type="text/javascript">
21382  */
21383  
21384
21385 /**
21386  * @class Roo.ComponentMgr
21387  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21388  * @singleton
21389  */
21390 Roo.ComponentMgr = function(){
21391     var all = new Roo.util.MixedCollection();
21392
21393     return {
21394         /**
21395          * Registers a component.
21396          * @param {Roo.Component} c The component
21397          */
21398         register : function(c){
21399             all.add(c);
21400         },
21401
21402         /**
21403          * Unregisters a component.
21404          * @param {Roo.Component} c The component
21405          */
21406         unregister : function(c){
21407             all.remove(c);
21408         },
21409
21410         /**
21411          * Returns a component by id
21412          * @param {String} id The component id
21413          */
21414         get : function(id){
21415             return all.get(id);
21416         },
21417
21418         /**
21419          * Registers a function that will be called when a specified component is added to ComponentMgr
21420          * @param {String} id The component id
21421          * @param {Funtction} fn The callback function
21422          * @param {Object} scope The scope of the callback
21423          */
21424         onAvailable : function(id, fn, scope){
21425             all.on("add", function(index, o){
21426                 if(o.id == id){
21427                     fn.call(scope || o, o);
21428                     all.un("add", fn, scope);
21429                 }
21430             });
21431         }
21432     };
21433 }();/*
21434  * Based on:
21435  * Ext JS Library 1.1.1
21436  * Copyright(c) 2006-2007, Ext JS, LLC.
21437  *
21438  * Originally Released Under LGPL - original licence link has changed is not relivant.
21439  *
21440  * Fork - LGPL
21441  * <script type="text/javascript">
21442  */
21443  
21444 /**
21445  * @class Roo.Component
21446  * @extends Roo.util.Observable
21447  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21448  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21449  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21450  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21451  * All visual components (widgets) that require rendering into a layout should subclass Component.
21452  * @constructor
21453  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21454  * 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
21455  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21456  */
21457 Roo.Component = function(config){
21458     config = config || {};
21459     if(config.tagName || config.dom || typeof config == "string"){ // element object
21460         config = {el: config, id: config.id || config};
21461     }
21462     this.initialConfig = config;
21463
21464     Roo.apply(this, config);
21465     this.addEvents({
21466         /**
21467          * @event disable
21468          * Fires after the component is disabled.
21469              * @param {Roo.Component} this
21470              */
21471         disable : true,
21472         /**
21473          * @event enable
21474          * Fires after the component is enabled.
21475              * @param {Roo.Component} this
21476              */
21477         enable : true,
21478         /**
21479          * @event beforeshow
21480          * Fires before the component is shown.  Return false to stop the show.
21481              * @param {Roo.Component} this
21482              */
21483         beforeshow : true,
21484         /**
21485          * @event show
21486          * Fires after the component is shown.
21487              * @param {Roo.Component} this
21488              */
21489         show : true,
21490         /**
21491          * @event beforehide
21492          * Fires before the component is hidden. Return false to stop the hide.
21493              * @param {Roo.Component} this
21494              */
21495         beforehide : true,
21496         /**
21497          * @event hide
21498          * Fires after the component is hidden.
21499              * @param {Roo.Component} this
21500              */
21501         hide : true,
21502         /**
21503          * @event beforerender
21504          * Fires before the component is rendered. Return false to stop the render.
21505              * @param {Roo.Component} this
21506              */
21507         beforerender : true,
21508         /**
21509          * @event render
21510          * Fires after the component is rendered.
21511              * @param {Roo.Component} this
21512              */
21513         render : true,
21514         /**
21515          * @event beforedestroy
21516          * Fires before the component is destroyed. Return false to stop the destroy.
21517              * @param {Roo.Component} this
21518              */
21519         beforedestroy : true,
21520         /**
21521          * @event destroy
21522          * Fires after the component is destroyed.
21523              * @param {Roo.Component} this
21524              */
21525         destroy : true
21526     });
21527     if(!this.id){
21528         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21529     }
21530     Roo.ComponentMgr.register(this);
21531     Roo.Component.superclass.constructor.call(this);
21532     this.initComponent();
21533     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21534         this.render(this.renderTo);
21535         delete this.renderTo;
21536     }
21537 };
21538
21539 // private
21540 Roo.Component.AUTO_ID = 1000;
21541
21542 Roo.extend(Roo.Component, Roo.util.Observable, {
21543     /**
21544      * @property {Boolean} hidden
21545      * true if this component is hidden. Read-only.
21546      */
21547     hidden : false,
21548     /**
21549      * true if this component is disabled. Read-only.
21550      */
21551     disabled : false,
21552     /**
21553      * true if this component has been rendered. Read-only.
21554      */
21555     rendered : false,
21556     
21557     /** @cfg {String} disableClass
21558      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21559      */
21560     disabledClass : "x-item-disabled",
21561         /** @cfg {Boolean} allowDomMove
21562          * Whether the component can move the Dom node when rendering (defaults to true).
21563          */
21564     allowDomMove : true,
21565     /** @cfg {String} hideMode
21566      * How this component should hidden. Supported values are
21567      * "visibility" (css visibility), "offsets" (negative offset position) and
21568      * "display" (css display) - defaults to "display".
21569      */
21570     hideMode: 'display',
21571
21572     // private
21573     ctype : "Roo.Component",
21574
21575     /** @cfg {String} actionMode 
21576      * which property holds the element that used for  hide() / show() / disable() / enable()
21577      * default is 'el' 
21578      */
21579     actionMode : "el",
21580
21581     // private
21582     getActionEl : function(){
21583         return this[this.actionMode];
21584     },
21585
21586     initComponent : Roo.emptyFn,
21587     /**
21588      * If this is a lazy rendering component, render it to its container element.
21589      * @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.
21590      */
21591     render : function(container, position){
21592         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21593             if(!container && this.el){
21594                 this.el = Roo.get(this.el);
21595                 container = this.el.dom.parentNode;
21596                 this.allowDomMove = false;
21597             }
21598             this.container = Roo.get(container);
21599             this.rendered = true;
21600             if(position !== undefined){
21601                 if(typeof position == 'number'){
21602                     position = this.container.dom.childNodes[position];
21603                 }else{
21604                     position = Roo.getDom(position);
21605                 }
21606             }
21607             this.onRender(this.container, position || null);
21608             if(this.cls){
21609                 this.el.addClass(this.cls);
21610                 delete this.cls;
21611             }
21612             if(this.style){
21613                 this.el.applyStyles(this.style);
21614                 delete this.style;
21615             }
21616             this.fireEvent("render", this);
21617             this.afterRender(this.container);
21618             if(this.hidden){
21619                 this.hide();
21620             }
21621             if(this.disabled){
21622                 this.disable();
21623             }
21624         }
21625         return this;
21626     },
21627
21628     // private
21629     // default function is not really useful
21630     onRender : function(ct, position){
21631         if(this.el){
21632             this.el = Roo.get(this.el);
21633             if(this.allowDomMove !== false){
21634                 ct.dom.insertBefore(this.el.dom, position);
21635             }
21636         }
21637     },
21638
21639     // private
21640     getAutoCreate : function(){
21641         var cfg = typeof this.autoCreate == "object" ?
21642                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21643         if(this.id && !cfg.id){
21644             cfg.id = this.id;
21645         }
21646         return cfg;
21647     },
21648
21649     // private
21650     afterRender : Roo.emptyFn,
21651
21652     /**
21653      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21654      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21655      */
21656     destroy : function(){
21657         if(this.fireEvent("beforedestroy", this) !== false){
21658             this.purgeListeners();
21659             this.beforeDestroy();
21660             if(this.rendered){
21661                 this.el.removeAllListeners();
21662                 this.el.remove();
21663                 if(this.actionMode == "container"){
21664                     this.container.remove();
21665                 }
21666             }
21667             this.onDestroy();
21668             Roo.ComponentMgr.unregister(this);
21669             this.fireEvent("destroy", this);
21670         }
21671     },
21672
21673         // private
21674     beforeDestroy : function(){
21675
21676     },
21677
21678         // private
21679         onDestroy : function(){
21680
21681     },
21682
21683     /**
21684      * Returns the underlying {@link Roo.Element}.
21685      * @return {Roo.Element} The element
21686      */
21687     getEl : function(){
21688         return this.el;
21689     },
21690
21691     /**
21692      * Returns the id of this component.
21693      * @return {String}
21694      */
21695     getId : function(){
21696         return this.id;
21697     },
21698
21699     /**
21700      * Try to focus this component.
21701      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21702      * @return {Roo.Component} this
21703      */
21704     focus : function(selectText){
21705         if(this.rendered){
21706             this.el.focus();
21707             if(selectText === true){
21708                 this.el.dom.select();
21709             }
21710         }
21711         return this;
21712     },
21713
21714     // private
21715     blur : function(){
21716         if(this.rendered){
21717             this.el.blur();
21718         }
21719         return this;
21720     },
21721
21722     /**
21723      * Disable this component.
21724      * @return {Roo.Component} this
21725      */
21726     disable : function(){
21727         if(this.rendered){
21728             this.onDisable();
21729         }
21730         this.disabled = true;
21731         this.fireEvent("disable", this);
21732         return this;
21733     },
21734
21735         // private
21736     onDisable : function(){
21737         this.getActionEl().addClass(this.disabledClass);
21738         this.el.dom.disabled = true;
21739     },
21740
21741     /**
21742      * Enable this component.
21743      * @return {Roo.Component} this
21744      */
21745     enable : function(){
21746         if(this.rendered){
21747             this.onEnable();
21748         }
21749         this.disabled = false;
21750         this.fireEvent("enable", this);
21751         return this;
21752     },
21753
21754         // private
21755     onEnable : function(){
21756         this.getActionEl().removeClass(this.disabledClass);
21757         this.el.dom.disabled = false;
21758     },
21759
21760     /**
21761      * Convenience function for setting disabled/enabled by boolean.
21762      * @param {Boolean} disabled
21763      */
21764     setDisabled : function(disabled){
21765         this[disabled ? "disable" : "enable"]();
21766     },
21767
21768     /**
21769      * Show this component.
21770      * @return {Roo.Component} this
21771      */
21772     show: function(){
21773         if(this.fireEvent("beforeshow", this) !== false){
21774             this.hidden = false;
21775             if(this.rendered){
21776                 this.onShow();
21777             }
21778             this.fireEvent("show", this);
21779         }
21780         return this;
21781     },
21782
21783     // private
21784     onShow : function(){
21785         var ae = this.getActionEl();
21786         if(this.hideMode == 'visibility'){
21787             ae.dom.style.visibility = "visible";
21788         }else if(this.hideMode == 'offsets'){
21789             ae.removeClass('x-hidden');
21790         }else{
21791             ae.dom.style.display = "";
21792         }
21793     },
21794
21795     /**
21796      * Hide this component.
21797      * @return {Roo.Component} this
21798      */
21799     hide: function(){
21800         if(this.fireEvent("beforehide", this) !== false){
21801             this.hidden = true;
21802             if(this.rendered){
21803                 this.onHide();
21804             }
21805             this.fireEvent("hide", this);
21806         }
21807         return this;
21808     },
21809
21810     // private
21811     onHide : function(){
21812         var ae = this.getActionEl();
21813         if(this.hideMode == 'visibility'){
21814             ae.dom.style.visibility = "hidden";
21815         }else if(this.hideMode == 'offsets'){
21816             ae.addClass('x-hidden');
21817         }else{
21818             ae.dom.style.display = "none";
21819         }
21820     },
21821
21822     /**
21823      * Convenience function to hide or show this component by boolean.
21824      * @param {Boolean} visible True to show, false to hide
21825      * @return {Roo.Component} this
21826      */
21827     setVisible: function(visible){
21828         if(visible) {
21829             this.show();
21830         }else{
21831             this.hide();
21832         }
21833         return this;
21834     },
21835
21836     /**
21837      * Returns true if this component is visible.
21838      */
21839     isVisible : function(){
21840         return this.getActionEl().isVisible();
21841     },
21842
21843     cloneConfig : function(overrides){
21844         overrides = overrides || {};
21845         var id = overrides.id || Roo.id();
21846         var cfg = Roo.applyIf(overrides, this.initialConfig);
21847         cfg.id = id; // prevent dup id
21848         return new this.constructor(cfg);
21849     }
21850 });/*
21851  * Based on:
21852  * Ext JS Library 1.1.1
21853  * Copyright(c) 2006-2007, Ext JS, LLC.
21854  *
21855  * Originally Released Under LGPL - original licence link has changed is not relivant.
21856  *
21857  * Fork - LGPL
21858  * <script type="text/javascript">
21859  */
21860  (function(){ 
21861 /**
21862  * @class Roo.Layer
21863  * @extends Roo.Element
21864  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21865  * automatic maintaining of shadow/shim positions.
21866  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21867  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21868  * you can pass a string with a CSS class name. False turns off the shadow.
21869  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21870  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21871  * @cfg {String} cls CSS class to add to the element
21872  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21873  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21874  * @constructor
21875  * @param {Object} config An object with config options.
21876  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21877  */
21878
21879 Roo.Layer = function(config, existingEl){
21880     config = config || {};
21881     var dh = Roo.DomHelper;
21882     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21883     if(existingEl){
21884         this.dom = Roo.getDom(existingEl);
21885     }
21886     if(!this.dom){
21887         var o = config.dh || {tag: "div", cls: "x-layer"};
21888         this.dom = dh.append(pel, o);
21889     }
21890     if(config.cls){
21891         this.addClass(config.cls);
21892     }
21893     this.constrain = config.constrain !== false;
21894     this.visibilityMode = Roo.Element.VISIBILITY;
21895     if(config.id){
21896         this.id = this.dom.id = config.id;
21897     }else{
21898         this.id = Roo.id(this.dom);
21899     }
21900     this.zindex = config.zindex || this.getZIndex();
21901     this.position("absolute", this.zindex);
21902     if(config.shadow){
21903         this.shadowOffset = config.shadowOffset || 4;
21904         this.shadow = new Roo.Shadow({
21905             offset : this.shadowOffset,
21906             mode : config.shadow
21907         });
21908     }else{
21909         this.shadowOffset = 0;
21910     }
21911     this.useShim = config.shim !== false && Roo.useShims;
21912     this.useDisplay = config.useDisplay;
21913     this.hide();
21914 };
21915
21916 var supr = Roo.Element.prototype;
21917
21918 // shims are shared among layer to keep from having 100 iframes
21919 var shims = [];
21920
21921 Roo.extend(Roo.Layer, Roo.Element, {
21922
21923     getZIndex : function(){
21924         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21925     },
21926
21927     getShim : function(){
21928         if(!this.useShim){
21929             return null;
21930         }
21931         if(this.shim){
21932             return this.shim;
21933         }
21934         var shim = shims.shift();
21935         if(!shim){
21936             shim = this.createShim();
21937             shim.enableDisplayMode('block');
21938             shim.dom.style.display = 'none';
21939             shim.dom.style.visibility = 'visible';
21940         }
21941         var pn = this.dom.parentNode;
21942         if(shim.dom.parentNode != pn){
21943             pn.insertBefore(shim.dom, this.dom);
21944         }
21945         shim.setStyle('z-index', this.getZIndex()-2);
21946         this.shim = shim;
21947         return shim;
21948     },
21949
21950     hideShim : function(){
21951         if(this.shim){
21952             this.shim.setDisplayed(false);
21953             shims.push(this.shim);
21954             delete this.shim;
21955         }
21956     },
21957
21958     disableShadow : function(){
21959         if(this.shadow){
21960             this.shadowDisabled = true;
21961             this.shadow.hide();
21962             this.lastShadowOffset = this.shadowOffset;
21963             this.shadowOffset = 0;
21964         }
21965     },
21966
21967     enableShadow : function(show){
21968         if(this.shadow){
21969             this.shadowDisabled = false;
21970             this.shadowOffset = this.lastShadowOffset;
21971             delete this.lastShadowOffset;
21972             if(show){
21973                 this.sync(true);
21974             }
21975         }
21976     },
21977
21978     // private
21979     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21980     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21981     sync : function(doShow){
21982         var sw = this.shadow;
21983         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21984             var sh = this.getShim();
21985
21986             var w = this.getWidth(),
21987                 h = this.getHeight();
21988
21989             var l = this.getLeft(true),
21990                 t = this.getTop(true);
21991
21992             if(sw && !this.shadowDisabled){
21993                 if(doShow && !sw.isVisible()){
21994                     sw.show(this);
21995                 }else{
21996                     sw.realign(l, t, w, h);
21997                 }
21998                 if(sh){
21999                     if(doShow){
22000                        sh.show();
22001                     }
22002                     // fit the shim behind the shadow, so it is shimmed too
22003                     var a = sw.adjusts, s = sh.dom.style;
22004                     s.left = (Math.min(l, l+a.l))+"px";
22005                     s.top = (Math.min(t, t+a.t))+"px";
22006                     s.width = (w+a.w)+"px";
22007                     s.height = (h+a.h)+"px";
22008                 }
22009             }else if(sh){
22010                 if(doShow){
22011                    sh.show();
22012                 }
22013                 sh.setSize(w, h);
22014                 sh.setLeftTop(l, t);
22015             }
22016             
22017         }
22018     },
22019
22020     // private
22021     destroy : function(){
22022         this.hideShim();
22023         if(this.shadow){
22024             this.shadow.hide();
22025         }
22026         this.removeAllListeners();
22027         var pn = this.dom.parentNode;
22028         if(pn){
22029             pn.removeChild(this.dom);
22030         }
22031         Roo.Element.uncache(this.id);
22032     },
22033
22034     remove : function(){
22035         this.destroy();
22036     },
22037
22038     // private
22039     beginUpdate : function(){
22040         this.updating = true;
22041     },
22042
22043     // private
22044     endUpdate : function(){
22045         this.updating = false;
22046         this.sync(true);
22047     },
22048
22049     // private
22050     hideUnders : function(negOffset){
22051         if(this.shadow){
22052             this.shadow.hide();
22053         }
22054         this.hideShim();
22055     },
22056
22057     // private
22058     constrainXY : function(){
22059         if(this.constrain){
22060             var vw = Roo.lib.Dom.getViewWidth(),
22061                 vh = Roo.lib.Dom.getViewHeight();
22062             var s = Roo.get(document).getScroll();
22063
22064             var xy = this.getXY();
22065             var x = xy[0], y = xy[1];   
22066             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22067             // only move it if it needs it
22068             var moved = false;
22069             // first validate right/bottom
22070             if((x + w) > vw+s.left){
22071                 x = vw - w - this.shadowOffset;
22072                 moved = true;
22073             }
22074             if((y + h) > vh+s.top){
22075                 y = vh - h - this.shadowOffset;
22076                 moved = true;
22077             }
22078             // then make sure top/left isn't negative
22079             if(x < s.left){
22080                 x = s.left;
22081                 moved = true;
22082             }
22083             if(y < s.top){
22084                 y = s.top;
22085                 moved = true;
22086             }
22087             if(moved){
22088                 if(this.avoidY){
22089                     var ay = this.avoidY;
22090                     if(y <= ay && (y+h) >= ay){
22091                         y = ay-h-5;   
22092                     }
22093                 }
22094                 xy = [x, y];
22095                 this.storeXY(xy);
22096                 supr.setXY.call(this, xy);
22097                 this.sync();
22098             }
22099         }
22100     },
22101
22102     isVisible : function(){
22103         return this.visible;    
22104     },
22105
22106     // private
22107     showAction : function(){
22108         this.visible = true; // track visibility to prevent getStyle calls
22109         if(this.useDisplay === true){
22110             this.setDisplayed("");
22111         }else if(this.lastXY){
22112             supr.setXY.call(this, this.lastXY);
22113         }else if(this.lastLT){
22114             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22115         }
22116     },
22117
22118     // private
22119     hideAction : function(){
22120         this.visible = false;
22121         if(this.useDisplay === true){
22122             this.setDisplayed(false);
22123         }else{
22124             this.setLeftTop(-10000,-10000);
22125         }
22126     },
22127
22128     // overridden Element method
22129     setVisible : function(v, a, d, c, e){
22130         if(v){
22131             this.showAction();
22132         }
22133         if(a && v){
22134             var cb = function(){
22135                 this.sync(true);
22136                 if(c){
22137                     c();
22138                 }
22139             }.createDelegate(this);
22140             supr.setVisible.call(this, true, true, d, cb, e);
22141         }else{
22142             if(!v){
22143                 this.hideUnders(true);
22144             }
22145             var cb = c;
22146             if(a){
22147                 cb = function(){
22148                     this.hideAction();
22149                     if(c){
22150                         c();
22151                     }
22152                 }.createDelegate(this);
22153             }
22154             supr.setVisible.call(this, v, a, d, cb, e);
22155             if(v){
22156                 this.sync(true);
22157             }else if(!a){
22158                 this.hideAction();
22159             }
22160         }
22161     },
22162
22163     storeXY : function(xy){
22164         delete this.lastLT;
22165         this.lastXY = xy;
22166     },
22167
22168     storeLeftTop : function(left, top){
22169         delete this.lastXY;
22170         this.lastLT = [left, top];
22171     },
22172
22173     // private
22174     beforeFx : function(){
22175         this.beforeAction();
22176         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22177     },
22178
22179     // private
22180     afterFx : function(){
22181         Roo.Layer.superclass.afterFx.apply(this, arguments);
22182         this.sync(this.isVisible());
22183     },
22184
22185     // private
22186     beforeAction : function(){
22187         if(!this.updating && this.shadow){
22188             this.shadow.hide();
22189         }
22190     },
22191
22192     // overridden Element method
22193     setLeft : function(left){
22194         this.storeLeftTop(left, this.getTop(true));
22195         supr.setLeft.apply(this, arguments);
22196         this.sync();
22197     },
22198
22199     setTop : function(top){
22200         this.storeLeftTop(this.getLeft(true), top);
22201         supr.setTop.apply(this, arguments);
22202         this.sync();
22203     },
22204
22205     setLeftTop : function(left, top){
22206         this.storeLeftTop(left, top);
22207         supr.setLeftTop.apply(this, arguments);
22208         this.sync();
22209     },
22210
22211     setXY : function(xy, a, d, c, e){
22212         this.fixDisplay();
22213         this.beforeAction();
22214         this.storeXY(xy);
22215         var cb = this.createCB(c);
22216         supr.setXY.call(this, xy, a, d, cb, e);
22217         if(!a){
22218             cb();
22219         }
22220     },
22221
22222     // private
22223     createCB : function(c){
22224         var el = this;
22225         return function(){
22226             el.constrainXY();
22227             el.sync(true);
22228             if(c){
22229                 c();
22230             }
22231         };
22232     },
22233
22234     // overridden Element method
22235     setX : function(x, a, d, c, e){
22236         this.setXY([x, this.getY()], a, d, c, e);
22237     },
22238
22239     // overridden Element method
22240     setY : function(y, a, d, c, e){
22241         this.setXY([this.getX(), y], a, d, c, e);
22242     },
22243
22244     // overridden Element method
22245     setSize : function(w, h, a, d, c, e){
22246         this.beforeAction();
22247         var cb = this.createCB(c);
22248         supr.setSize.call(this, w, h, a, d, cb, e);
22249         if(!a){
22250             cb();
22251         }
22252     },
22253
22254     // overridden Element method
22255     setWidth : function(w, a, d, c, e){
22256         this.beforeAction();
22257         var cb = this.createCB(c);
22258         supr.setWidth.call(this, w, a, d, cb, e);
22259         if(!a){
22260             cb();
22261         }
22262     },
22263
22264     // overridden Element method
22265     setHeight : function(h, a, d, c, e){
22266         this.beforeAction();
22267         var cb = this.createCB(c);
22268         supr.setHeight.call(this, h, a, d, cb, e);
22269         if(!a){
22270             cb();
22271         }
22272     },
22273
22274     // overridden Element method
22275     setBounds : function(x, y, w, h, a, d, c, e){
22276         this.beforeAction();
22277         var cb = this.createCB(c);
22278         if(!a){
22279             this.storeXY([x, y]);
22280             supr.setXY.call(this, [x, y]);
22281             supr.setSize.call(this, w, h, a, d, cb, e);
22282             cb();
22283         }else{
22284             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22285         }
22286         return this;
22287     },
22288     
22289     /**
22290      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22291      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22292      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22293      * @param {Number} zindex The new z-index to set
22294      * @return {this} The Layer
22295      */
22296     setZIndex : function(zindex){
22297         this.zindex = zindex;
22298         this.setStyle("z-index", zindex + 2);
22299         if(this.shadow){
22300             this.shadow.setZIndex(zindex + 1);
22301         }
22302         if(this.shim){
22303             this.shim.setStyle("z-index", zindex);
22304         }
22305     }
22306 });
22307 })();/*
22308  * Based on:
22309  * Ext JS Library 1.1.1
22310  * Copyright(c) 2006-2007, Ext JS, LLC.
22311  *
22312  * Originally Released Under LGPL - original licence link has changed is not relivant.
22313  *
22314  * Fork - LGPL
22315  * <script type="text/javascript">
22316  */
22317
22318
22319 /**
22320  * @class Roo.Shadow
22321  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22322  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22323  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22324  * @constructor
22325  * Create a new Shadow
22326  * @param {Object} config The config object
22327  */
22328 Roo.Shadow = function(config){
22329     Roo.apply(this, config);
22330     if(typeof this.mode != "string"){
22331         this.mode = this.defaultMode;
22332     }
22333     var o = this.offset, a = {h: 0};
22334     var rad = Math.floor(this.offset/2);
22335     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22336         case "drop":
22337             a.w = 0;
22338             a.l = a.t = o;
22339             a.t -= 1;
22340             if(Roo.isIE){
22341                 a.l -= this.offset + rad;
22342                 a.t -= this.offset + rad;
22343                 a.w -= rad;
22344                 a.h -= rad;
22345                 a.t += 1;
22346             }
22347         break;
22348         case "sides":
22349             a.w = (o*2);
22350             a.l = -o;
22351             a.t = o-1;
22352             if(Roo.isIE){
22353                 a.l -= (this.offset - rad);
22354                 a.t -= this.offset + rad;
22355                 a.l += 1;
22356                 a.w -= (this.offset - rad)*2;
22357                 a.w -= rad + 1;
22358                 a.h -= 1;
22359             }
22360         break;
22361         case "frame":
22362             a.w = a.h = (o*2);
22363             a.l = a.t = -o;
22364             a.t += 1;
22365             a.h -= 2;
22366             if(Roo.isIE){
22367                 a.l -= (this.offset - rad);
22368                 a.t -= (this.offset - rad);
22369                 a.l += 1;
22370                 a.w -= (this.offset + rad + 1);
22371                 a.h -= (this.offset + rad);
22372                 a.h += 1;
22373             }
22374         break;
22375     };
22376
22377     this.adjusts = a;
22378 };
22379
22380 Roo.Shadow.prototype = {
22381     /**
22382      * @cfg {String} mode
22383      * The shadow display mode.  Supports the following options:<br />
22384      * sides: Shadow displays on both sides and bottom only<br />
22385      * frame: Shadow displays equally on all four sides<br />
22386      * drop: Traditional bottom-right drop shadow (default)
22387      */
22388     /**
22389      * @cfg {String} offset
22390      * The number of pixels to offset the shadow from the element (defaults to 4)
22391      */
22392     offset: 4,
22393
22394     // private
22395     defaultMode: "drop",
22396
22397     /**
22398      * Displays the shadow under the target element
22399      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22400      */
22401     show : function(target){
22402         target = Roo.get(target);
22403         if(!this.el){
22404             this.el = Roo.Shadow.Pool.pull();
22405             if(this.el.dom.nextSibling != target.dom){
22406                 this.el.insertBefore(target);
22407             }
22408         }
22409         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22410         if(Roo.isIE){
22411             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22412         }
22413         this.realign(
22414             target.getLeft(true),
22415             target.getTop(true),
22416             target.getWidth(),
22417             target.getHeight()
22418         );
22419         this.el.dom.style.display = "block";
22420     },
22421
22422     /**
22423      * Returns true if the shadow is visible, else false
22424      */
22425     isVisible : function(){
22426         return this.el ? true : false;  
22427     },
22428
22429     /**
22430      * Direct alignment when values are already available. Show must be called at least once before
22431      * calling this method to ensure it is initialized.
22432      * @param {Number} left The target element left position
22433      * @param {Number} top The target element top position
22434      * @param {Number} width The target element width
22435      * @param {Number} height The target element height
22436      */
22437     realign : function(l, t, w, h){
22438         if(!this.el){
22439             return;
22440         }
22441         var a = this.adjusts, d = this.el.dom, s = d.style;
22442         var iea = 0;
22443         s.left = (l+a.l)+"px";
22444         s.top = (t+a.t)+"px";
22445         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22446         if(s.width != sws || s.height != shs){
22447             s.width = sws;
22448             s.height = shs;
22449             if(!Roo.isIE){
22450                 var cn = d.childNodes;
22451                 var sww = Math.max(0, (sw-12))+"px";
22452                 cn[0].childNodes[1].style.width = sww;
22453                 cn[1].childNodes[1].style.width = sww;
22454                 cn[2].childNodes[1].style.width = sww;
22455                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22456             }
22457         }
22458     },
22459
22460     /**
22461      * Hides this shadow
22462      */
22463     hide : function(){
22464         if(this.el){
22465             this.el.dom.style.display = "none";
22466             Roo.Shadow.Pool.push(this.el);
22467             delete this.el;
22468         }
22469     },
22470
22471     /**
22472      * Adjust the z-index of this shadow
22473      * @param {Number} zindex The new z-index
22474      */
22475     setZIndex : function(z){
22476         this.zIndex = z;
22477         if(this.el){
22478             this.el.setStyle("z-index", z);
22479         }
22480     }
22481 };
22482
22483 // Private utility class that manages the internal Shadow cache
22484 Roo.Shadow.Pool = function(){
22485     var p = [];
22486     var markup = Roo.isIE ?
22487                  '<div class="x-ie-shadow"></div>' :
22488                  '<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>';
22489     return {
22490         pull : function(){
22491             var sh = p.shift();
22492             if(!sh){
22493                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22494                 sh.autoBoxAdjust = false;
22495             }
22496             return sh;
22497         },
22498
22499         push : function(sh){
22500             p.push(sh);
22501         }
22502     };
22503 }();/*
22504  * Based on:
22505  * Ext JS Library 1.1.1
22506  * Copyright(c) 2006-2007, Ext JS, LLC.
22507  *
22508  * Originally Released Under LGPL - original licence link has changed is not relivant.
22509  *
22510  * Fork - LGPL
22511  * <script type="text/javascript">
22512  */
22513
22514 /**
22515  * @class Roo.BoxComponent
22516  * @extends Roo.Component
22517  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22518  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22519  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22520  * layout containers.
22521  * @constructor
22522  * @param {Roo.Element/String/Object} config The configuration options.
22523  */
22524 Roo.BoxComponent = function(config){
22525     Roo.Component.call(this, config);
22526     this.addEvents({
22527         /**
22528          * @event resize
22529          * Fires after the component is resized.
22530              * @param {Roo.Component} this
22531              * @param {Number} adjWidth The box-adjusted width that was set
22532              * @param {Number} adjHeight The box-adjusted height that was set
22533              * @param {Number} rawWidth The width that was originally specified
22534              * @param {Number} rawHeight The height that was originally specified
22535              */
22536         resize : true,
22537         /**
22538          * @event move
22539          * Fires after the component is moved.
22540              * @param {Roo.Component} this
22541              * @param {Number} x The new x position
22542              * @param {Number} y The new y position
22543              */
22544         move : true
22545     });
22546 };
22547
22548 Roo.extend(Roo.BoxComponent, Roo.Component, {
22549     // private, set in afterRender to signify that the component has been rendered
22550     boxReady : false,
22551     // private, used to defer height settings to subclasses
22552     deferHeight: false,
22553     /** @cfg {Number} width
22554      * width (optional) size of component
22555      */
22556      /** @cfg {Number} height
22557      * height (optional) size of component
22558      */
22559      
22560     /**
22561      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22562      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22563      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22564      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22565      * @return {Roo.BoxComponent} this
22566      */
22567     setSize : function(w, h){
22568         // support for standard size objects
22569         if(typeof w == 'object'){
22570             h = w.height;
22571             w = w.width;
22572         }
22573         // not rendered
22574         if(!this.boxReady){
22575             this.width = w;
22576             this.height = h;
22577             return this;
22578         }
22579
22580         // prevent recalcs when not needed
22581         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22582             return this;
22583         }
22584         this.lastSize = {width: w, height: h};
22585
22586         var adj = this.adjustSize(w, h);
22587         var aw = adj.width, ah = adj.height;
22588         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22589             var rz = this.getResizeEl();
22590             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22591                 rz.setSize(aw, ah);
22592             }else if(!this.deferHeight && ah !== undefined){
22593                 rz.setHeight(ah);
22594             }else if(aw !== undefined){
22595                 rz.setWidth(aw);
22596             }
22597             this.onResize(aw, ah, w, h);
22598             this.fireEvent('resize', this, aw, ah, w, h);
22599         }
22600         return this;
22601     },
22602
22603     /**
22604      * Gets the current size of the component's underlying element.
22605      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22606      */
22607     getSize : function(){
22608         return this.el.getSize();
22609     },
22610
22611     /**
22612      * Gets the current XY position of the component's underlying element.
22613      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22614      * @return {Array} The XY position of the element (e.g., [100, 200])
22615      */
22616     getPosition : function(local){
22617         if(local === true){
22618             return [this.el.getLeft(true), this.el.getTop(true)];
22619         }
22620         return this.xy || this.el.getXY();
22621     },
22622
22623     /**
22624      * Gets the current box measurements of the component's underlying element.
22625      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22626      * @returns {Object} box An object in the format {x, y, width, height}
22627      */
22628     getBox : function(local){
22629         var s = this.el.getSize();
22630         if(local){
22631             s.x = this.el.getLeft(true);
22632             s.y = this.el.getTop(true);
22633         }else{
22634             var xy = this.xy || this.el.getXY();
22635             s.x = xy[0];
22636             s.y = xy[1];
22637         }
22638         return s;
22639     },
22640
22641     /**
22642      * Sets the current box measurements of the component's underlying element.
22643      * @param {Object} box An object in the format {x, y, width, height}
22644      * @returns {Roo.BoxComponent} this
22645      */
22646     updateBox : function(box){
22647         this.setSize(box.width, box.height);
22648         this.setPagePosition(box.x, box.y);
22649         return this;
22650     },
22651
22652     // protected
22653     getResizeEl : function(){
22654         return this.resizeEl || this.el;
22655     },
22656
22657     // protected
22658     getPositionEl : function(){
22659         return this.positionEl || this.el;
22660     },
22661
22662     /**
22663      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22664      * This method fires the move event.
22665      * @param {Number} left The new left
22666      * @param {Number} top The new top
22667      * @returns {Roo.BoxComponent} this
22668      */
22669     setPosition : function(x, y){
22670         this.x = x;
22671         this.y = y;
22672         if(!this.boxReady){
22673             return this;
22674         }
22675         var adj = this.adjustPosition(x, y);
22676         var ax = adj.x, ay = adj.y;
22677
22678         var el = this.getPositionEl();
22679         if(ax !== undefined || ay !== undefined){
22680             if(ax !== undefined && ay !== undefined){
22681                 el.setLeftTop(ax, ay);
22682             }else if(ax !== undefined){
22683                 el.setLeft(ax);
22684             }else if(ay !== undefined){
22685                 el.setTop(ay);
22686             }
22687             this.onPosition(ax, ay);
22688             this.fireEvent('move', this, ax, ay);
22689         }
22690         return this;
22691     },
22692
22693     /**
22694      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22695      * This method fires the move event.
22696      * @param {Number} x The new x position
22697      * @param {Number} y The new y position
22698      * @returns {Roo.BoxComponent} this
22699      */
22700     setPagePosition : function(x, y){
22701         this.pageX = x;
22702         this.pageY = y;
22703         if(!this.boxReady){
22704             return;
22705         }
22706         if(x === undefined || y === undefined){ // cannot translate undefined points
22707             return;
22708         }
22709         var p = this.el.translatePoints(x, y);
22710         this.setPosition(p.left, p.top);
22711         return this;
22712     },
22713
22714     // private
22715     onRender : function(ct, position){
22716         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22717         if(this.resizeEl){
22718             this.resizeEl = Roo.get(this.resizeEl);
22719         }
22720         if(this.positionEl){
22721             this.positionEl = Roo.get(this.positionEl);
22722         }
22723     },
22724
22725     // private
22726     afterRender : function(){
22727         Roo.BoxComponent.superclass.afterRender.call(this);
22728         this.boxReady = true;
22729         this.setSize(this.width, this.height);
22730         if(this.x || this.y){
22731             this.setPosition(this.x, this.y);
22732         }
22733         if(this.pageX || this.pageY){
22734             this.setPagePosition(this.pageX, this.pageY);
22735         }
22736     },
22737
22738     /**
22739      * Force the component's size to recalculate based on the underlying element's current height and width.
22740      * @returns {Roo.BoxComponent} this
22741      */
22742     syncSize : function(){
22743         delete this.lastSize;
22744         this.setSize(this.el.getWidth(), this.el.getHeight());
22745         return this;
22746     },
22747
22748     /**
22749      * Called after the component is resized, this method is empty by default but can be implemented by any
22750      * subclass that needs to perform custom logic after a resize occurs.
22751      * @param {Number} adjWidth The box-adjusted width that was set
22752      * @param {Number} adjHeight The box-adjusted height that was set
22753      * @param {Number} rawWidth The width that was originally specified
22754      * @param {Number} rawHeight The height that was originally specified
22755      */
22756     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22757
22758     },
22759
22760     /**
22761      * Called after the component is moved, this method is empty by default but can be implemented by any
22762      * subclass that needs to perform custom logic after a move occurs.
22763      * @param {Number} x The new x position
22764      * @param {Number} y The new y position
22765      */
22766     onPosition : function(x, y){
22767
22768     },
22769
22770     // private
22771     adjustSize : function(w, h){
22772         if(this.autoWidth){
22773             w = 'auto';
22774         }
22775         if(this.autoHeight){
22776             h = 'auto';
22777         }
22778         return {width : w, height: h};
22779     },
22780
22781     // private
22782     adjustPosition : function(x, y){
22783         return {x : x, y: y};
22784     }
22785 });/*
22786  * Based on:
22787  * Ext JS Library 1.1.1
22788  * Copyright(c) 2006-2007, Ext JS, LLC.
22789  *
22790  * Originally Released Under LGPL - original licence link has changed is not relivant.
22791  *
22792  * Fork - LGPL
22793  * <script type="text/javascript">
22794  */
22795
22796
22797 /**
22798  * @class Roo.SplitBar
22799  * @extends Roo.util.Observable
22800  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22801  * <br><br>
22802  * Usage:
22803  * <pre><code>
22804 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22805                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22806 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22807 split.minSize = 100;
22808 split.maxSize = 600;
22809 split.animate = true;
22810 split.on('moved', splitterMoved);
22811 </code></pre>
22812  * @constructor
22813  * Create a new SplitBar
22814  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22815  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22816  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22817  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22818                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22819                         position of the SplitBar).
22820  */
22821 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22822     
22823     /** @private */
22824     this.el = Roo.get(dragElement, true);
22825     this.el.dom.unselectable = "on";
22826     /** @private */
22827     this.resizingEl = Roo.get(resizingElement, true);
22828
22829     /**
22830      * @private
22831      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22832      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22833      * @type Number
22834      */
22835     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22836     
22837     /**
22838      * The minimum size of the resizing element. (Defaults to 0)
22839      * @type Number
22840      */
22841     this.minSize = 0;
22842     
22843     /**
22844      * The maximum size of the resizing element. (Defaults to 2000)
22845      * @type Number
22846      */
22847     this.maxSize = 2000;
22848     
22849     /**
22850      * Whether to animate the transition to the new size
22851      * @type Boolean
22852      */
22853     this.animate = false;
22854     
22855     /**
22856      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22857      * @type Boolean
22858      */
22859     this.useShim = false;
22860     
22861     /** @private */
22862     this.shim = null;
22863     
22864     if(!existingProxy){
22865         /** @private */
22866         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22867     }else{
22868         this.proxy = Roo.get(existingProxy).dom;
22869     }
22870     /** @private */
22871     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22872     
22873     /** @private */
22874     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22875     
22876     /** @private */
22877     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22878     
22879     /** @private */
22880     this.dragSpecs = {};
22881     
22882     /**
22883      * @private The adapter to use to positon and resize elements
22884      */
22885     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22886     this.adapter.init(this);
22887     
22888     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22889         /** @private */
22890         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22891         this.el.addClass("x-splitbar-h");
22892     }else{
22893         /** @private */
22894         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22895         this.el.addClass("x-splitbar-v");
22896     }
22897     
22898     this.addEvents({
22899         /**
22900          * @event resize
22901          * Fires when the splitter is moved (alias for {@link #event-moved})
22902          * @param {Roo.SplitBar} this
22903          * @param {Number} newSize the new width or height
22904          */
22905         "resize" : true,
22906         /**
22907          * @event moved
22908          * Fires when the splitter is moved
22909          * @param {Roo.SplitBar} this
22910          * @param {Number} newSize the new width or height
22911          */
22912         "moved" : true,
22913         /**
22914          * @event beforeresize
22915          * Fires before the splitter is dragged
22916          * @param {Roo.SplitBar} this
22917          */
22918         "beforeresize" : true,
22919
22920         "beforeapply" : true
22921     });
22922
22923     Roo.util.Observable.call(this);
22924 };
22925
22926 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22927     onStartProxyDrag : function(x, y){
22928         this.fireEvent("beforeresize", this);
22929         if(!this.overlay){
22930             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22931             o.unselectable();
22932             o.enableDisplayMode("block");
22933             // all splitbars share the same overlay
22934             Roo.SplitBar.prototype.overlay = o;
22935         }
22936         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22937         this.overlay.show();
22938         Roo.get(this.proxy).setDisplayed("block");
22939         var size = this.adapter.getElementSize(this);
22940         this.activeMinSize = this.getMinimumSize();;
22941         this.activeMaxSize = this.getMaximumSize();;
22942         var c1 = size - this.activeMinSize;
22943         var c2 = Math.max(this.activeMaxSize - size, 0);
22944         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22945             this.dd.resetConstraints();
22946             this.dd.setXConstraint(
22947                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22948                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22949             );
22950             this.dd.setYConstraint(0, 0);
22951         }else{
22952             this.dd.resetConstraints();
22953             this.dd.setXConstraint(0, 0);
22954             this.dd.setYConstraint(
22955                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22956                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22957             );
22958          }
22959         this.dragSpecs.startSize = size;
22960         this.dragSpecs.startPoint = [x, y];
22961         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22962     },
22963     
22964     /** 
22965      * @private Called after the drag operation by the DDProxy
22966      */
22967     onEndProxyDrag : function(e){
22968         Roo.get(this.proxy).setDisplayed(false);
22969         var endPoint = Roo.lib.Event.getXY(e);
22970         if(this.overlay){
22971             this.overlay.hide();
22972         }
22973         var newSize;
22974         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22975             newSize = this.dragSpecs.startSize + 
22976                 (this.placement == Roo.SplitBar.LEFT ?
22977                     endPoint[0] - this.dragSpecs.startPoint[0] :
22978                     this.dragSpecs.startPoint[0] - endPoint[0]
22979                 );
22980         }else{
22981             newSize = this.dragSpecs.startSize + 
22982                 (this.placement == Roo.SplitBar.TOP ?
22983                     endPoint[1] - this.dragSpecs.startPoint[1] :
22984                     this.dragSpecs.startPoint[1] - endPoint[1]
22985                 );
22986         }
22987         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22988         if(newSize != this.dragSpecs.startSize){
22989             if(this.fireEvent('beforeapply', this, newSize) !== false){
22990                 this.adapter.setElementSize(this, newSize);
22991                 this.fireEvent("moved", this, newSize);
22992                 this.fireEvent("resize", this, newSize);
22993             }
22994         }
22995     },
22996     
22997     /**
22998      * Get the adapter this SplitBar uses
22999      * @return The adapter object
23000      */
23001     getAdapter : function(){
23002         return this.adapter;
23003     },
23004     
23005     /**
23006      * Set the adapter this SplitBar uses
23007      * @param {Object} adapter A SplitBar adapter object
23008      */
23009     setAdapter : function(adapter){
23010         this.adapter = adapter;
23011         this.adapter.init(this);
23012     },
23013     
23014     /**
23015      * Gets the minimum size for the resizing element
23016      * @return {Number} The minimum size
23017      */
23018     getMinimumSize : function(){
23019         return this.minSize;
23020     },
23021     
23022     /**
23023      * Sets the minimum size for the resizing element
23024      * @param {Number} minSize The minimum size
23025      */
23026     setMinimumSize : function(minSize){
23027         this.minSize = minSize;
23028     },
23029     
23030     /**
23031      * Gets the maximum size for the resizing element
23032      * @return {Number} The maximum size
23033      */
23034     getMaximumSize : function(){
23035         return this.maxSize;
23036     },
23037     
23038     /**
23039      * Sets the maximum size for the resizing element
23040      * @param {Number} maxSize The maximum size
23041      */
23042     setMaximumSize : function(maxSize){
23043         this.maxSize = maxSize;
23044     },
23045     
23046     /**
23047      * Sets the initialize size for the resizing element
23048      * @param {Number} size The initial size
23049      */
23050     setCurrentSize : function(size){
23051         var oldAnimate = this.animate;
23052         this.animate = false;
23053         this.adapter.setElementSize(this, size);
23054         this.animate = oldAnimate;
23055     },
23056     
23057     /**
23058      * Destroy this splitbar. 
23059      * @param {Boolean} removeEl True to remove the element
23060      */
23061     destroy : function(removeEl){
23062         if(this.shim){
23063             this.shim.remove();
23064         }
23065         this.dd.unreg();
23066         this.proxy.parentNode.removeChild(this.proxy);
23067         if(removeEl){
23068             this.el.remove();
23069         }
23070     }
23071 });
23072
23073 /**
23074  * @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.
23075  */
23076 Roo.SplitBar.createProxy = function(dir){
23077     var proxy = new Roo.Element(document.createElement("div"));
23078     proxy.unselectable();
23079     var cls = 'x-splitbar-proxy';
23080     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23081     document.body.appendChild(proxy.dom);
23082     return proxy.dom;
23083 };
23084
23085 /** 
23086  * @class Roo.SplitBar.BasicLayoutAdapter
23087  * Default Adapter. It assumes the splitter and resizing element are not positioned
23088  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23089  */
23090 Roo.SplitBar.BasicLayoutAdapter = function(){
23091 };
23092
23093 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23094     // do nothing for now
23095     init : function(s){
23096     
23097     },
23098     /**
23099      * Called before drag operations to get the current size of the resizing element. 
23100      * @param {Roo.SplitBar} s The SplitBar using this adapter
23101      */
23102      getElementSize : function(s){
23103         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23104             return s.resizingEl.getWidth();
23105         }else{
23106             return s.resizingEl.getHeight();
23107         }
23108     },
23109     
23110     /**
23111      * Called after drag operations to set the size of the resizing element.
23112      * @param {Roo.SplitBar} s The SplitBar using this adapter
23113      * @param {Number} newSize The new size to set
23114      * @param {Function} onComplete A function to be invoked when resizing is complete
23115      */
23116     setElementSize : function(s, newSize, onComplete){
23117         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23118             if(!s.animate){
23119                 s.resizingEl.setWidth(newSize);
23120                 if(onComplete){
23121                     onComplete(s, newSize);
23122                 }
23123             }else{
23124                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23125             }
23126         }else{
23127             
23128             if(!s.animate){
23129                 s.resizingEl.setHeight(newSize);
23130                 if(onComplete){
23131                     onComplete(s, newSize);
23132                 }
23133             }else{
23134                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23135             }
23136         }
23137     }
23138 };
23139
23140 /** 
23141  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23142  * @extends Roo.SplitBar.BasicLayoutAdapter
23143  * Adapter that  moves the splitter element to align with the resized sizing element. 
23144  * Used with an absolute positioned SplitBar.
23145  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23146  * document.body, make sure you assign an id to the body element.
23147  */
23148 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23149     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23150     this.container = Roo.get(container);
23151 };
23152
23153 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23154     init : function(s){
23155         this.basic.init(s);
23156     },
23157     
23158     getElementSize : function(s){
23159         return this.basic.getElementSize(s);
23160     },
23161     
23162     setElementSize : function(s, newSize, onComplete){
23163         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23164     },
23165     
23166     moveSplitter : function(s){
23167         var yes = Roo.SplitBar;
23168         switch(s.placement){
23169             case yes.LEFT:
23170                 s.el.setX(s.resizingEl.getRight());
23171                 break;
23172             case yes.RIGHT:
23173                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23174                 break;
23175             case yes.TOP:
23176                 s.el.setY(s.resizingEl.getBottom());
23177                 break;
23178             case yes.BOTTOM:
23179                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23180                 break;
23181         }
23182     }
23183 };
23184
23185 /**
23186  * Orientation constant - Create a vertical SplitBar
23187  * @static
23188  * @type Number
23189  */
23190 Roo.SplitBar.VERTICAL = 1;
23191
23192 /**
23193  * Orientation constant - Create a horizontal SplitBar
23194  * @static
23195  * @type Number
23196  */
23197 Roo.SplitBar.HORIZONTAL = 2;
23198
23199 /**
23200  * Placement constant - The resizing element is to the left of the splitter element
23201  * @static
23202  * @type Number
23203  */
23204 Roo.SplitBar.LEFT = 1;
23205
23206 /**
23207  * Placement constant - The resizing element is to the right of the splitter element
23208  * @static
23209  * @type Number
23210  */
23211 Roo.SplitBar.RIGHT = 2;
23212
23213 /**
23214  * Placement constant - The resizing element is positioned above the splitter element
23215  * @static
23216  * @type Number
23217  */
23218 Roo.SplitBar.TOP = 3;
23219
23220 /**
23221  * Placement constant - The resizing element is positioned under splitter element
23222  * @static
23223  * @type Number
23224  */
23225 Roo.SplitBar.BOTTOM = 4;
23226 /*
23227  * Based on:
23228  * Ext JS Library 1.1.1
23229  * Copyright(c) 2006-2007, Ext JS, LLC.
23230  *
23231  * Originally Released Under LGPL - original licence link has changed is not relivant.
23232  *
23233  * Fork - LGPL
23234  * <script type="text/javascript">
23235  */
23236
23237 /**
23238  * @class Roo.View
23239  * @extends Roo.util.Observable
23240  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23241  * This class also supports single and multi selection modes. <br>
23242  * Create a data model bound view:
23243  <pre><code>
23244  var store = new Roo.data.Store(...);
23245
23246  var view = new Roo.View("my-element",
23247  '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23248  {
23249  singleSelect: true,
23250  selectedClass: "ydataview-selected",
23251  store: store
23252  });
23253
23254  // listen for node click?
23255  view.on("click", function(vw, index, node, e){
23256  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23257  });
23258
23259  // load XML data
23260  dataModel.load("foobar.xml");
23261  </code></pre>
23262  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23263  * <br><br>
23264  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23265  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23266  * @constructor
23267  * Create a new View
23268  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23269  * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
23270  * @param {Object} config The config object
23271  */
23272 Roo.View = function(container, tpl, config){
23273     this.el = Roo.get(container);
23274     if(typeof tpl == "string"){
23275         tpl = new Roo.Template(tpl);
23276     }
23277     tpl.compile();
23278     /**
23279      * The template used by this View
23280      * @type {Roo.DomHelper.Template}
23281      */
23282     this.tpl = tpl;
23283
23284     Roo.apply(this, config);
23285
23286     /** @private */
23287     this.addEvents({
23288     /**
23289      * @event beforeclick
23290      * Fires before a click is processed. Returns false to cancel the default action.
23291      * @param {Roo.View} this
23292      * @param {Number} index The index of the target node
23293      * @param {HTMLElement} node The target node
23294      * @param {Roo.EventObject} e The raw event object
23295      */
23296         "beforeclick" : true,
23297     /**
23298      * @event click
23299      * Fires when a template node is clicked.
23300      * @param {Roo.View} this
23301      * @param {Number} index The index of the target node
23302      * @param {HTMLElement} node The target node
23303      * @param {Roo.EventObject} e The raw event object
23304      */
23305         "click" : true,
23306     /**
23307      * @event dblclick
23308      * Fires when a template node is double clicked.
23309      * @param {Roo.View} this
23310      * @param {Number} index The index of the target node
23311      * @param {HTMLElement} node The target node
23312      * @param {Roo.EventObject} e The raw event object
23313      */
23314         "dblclick" : true,
23315     /**
23316      * @event contextmenu
23317      * Fires when a template node is right clicked.
23318      * @param {Roo.View} this
23319      * @param {Number} index The index of the target node
23320      * @param {HTMLElement} node The target node
23321      * @param {Roo.EventObject} e The raw event object
23322      */
23323         "contextmenu" : true,
23324     /**
23325      * @event selectionchange
23326      * Fires when the selected nodes change.
23327      * @param {Roo.View} this
23328      * @param {Array} selections Array of the selected nodes
23329      */
23330         "selectionchange" : true,
23331
23332     /**
23333      * @event beforeselect
23334      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23335      * @param {Roo.View} this
23336      * @param {HTMLElement} node The node to be selected
23337      * @param {Array} selections Array of currently selected nodes
23338      */
23339         "beforeselect" : true
23340     });
23341
23342     this.el.on({
23343         "click": this.onClick,
23344         "dblclick": this.onDblClick,
23345         "contextmenu": this.onContextMenu,
23346         scope:this
23347     });
23348
23349     this.selections = [];
23350     this.nodes = [];
23351     this.cmp = new Roo.CompositeElementLite([]);
23352     if(this.store){
23353         this.store = Roo.factory(this.store, Roo.data);
23354         this.setStore(this.store, true);
23355     }
23356     Roo.View.superclass.constructor.call(this);
23357 };
23358
23359 Roo.extend(Roo.View, Roo.util.Observable, {
23360     /**
23361      * The css class to add to selected nodes
23362      * @type {Roo.DomHelper.Template}
23363      */
23364     selectedClass : "x-view-selected",
23365     
23366     emptyText : "",
23367     /**
23368      * Returns the element this view is bound to.
23369      * @return {Roo.Element}
23370      */
23371     getEl : function(){
23372         return this.el;
23373     },
23374
23375     /**
23376      * Refreshes the view.
23377      */
23378     refresh : function(){
23379         var t = this.tpl;
23380         this.clearSelections();
23381         this.el.update("");
23382         var html = [];
23383         var records = this.store.getRange();
23384         if(records.length < 1){
23385             this.el.update(this.emptyText);
23386             return;
23387         }
23388         for(var i = 0, len = records.length; i < len; i++){
23389             var data = this.prepareData(records[i].data, i, records[i]);
23390             html[html.length] = t.apply(data);
23391         }
23392         this.el.update(html.join(""));
23393         this.nodes = this.el.dom.childNodes;
23394         this.updateIndexes(0);
23395     },
23396
23397     /**
23398      * Function to override to reformat the data that is sent to
23399      * the template for each node.
23400      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23401      * a JSON object for an UpdateManager bound view).
23402      */
23403     prepareData : function(data){
23404         return data;
23405     },
23406
23407     onUpdate : function(ds, record){
23408         this.clearSelections();
23409         var index = this.store.indexOf(record);
23410         var n = this.nodes[index];
23411         this.tpl.insertBefore(n, this.prepareData(record.data));
23412         n.parentNode.removeChild(n);
23413         this.updateIndexes(index, index);
23414     },
23415
23416     onAdd : function(ds, records, index){
23417         this.clearSelections();
23418         if(this.nodes.length == 0){
23419             this.refresh();
23420             return;
23421         }
23422         var n = this.nodes[index];
23423         for(var i = 0, len = records.length; i < len; i++){
23424             var d = this.prepareData(records[i].data);
23425             if(n){
23426                 this.tpl.insertBefore(n, d);
23427             }else{
23428                 this.tpl.append(this.el, d);
23429             }
23430         }
23431         this.updateIndexes(index);
23432     },
23433
23434     onRemove : function(ds, record, index){
23435         this.clearSelections();
23436         this.el.dom.removeChild(this.nodes[index]);
23437         this.updateIndexes(index);
23438     },
23439
23440     /**
23441      * Refresh an individual node.
23442      * @param {Number} index
23443      */
23444     refreshNode : function(index){
23445         this.onUpdate(this.store, this.store.getAt(index));
23446     },
23447
23448     updateIndexes : function(startIndex, endIndex){
23449         var ns = this.nodes;
23450         startIndex = startIndex || 0;
23451         endIndex = endIndex || ns.length - 1;
23452         for(var i = startIndex; i <= endIndex; i++){
23453             ns[i].nodeIndex = i;
23454         }
23455     },
23456
23457     /**
23458      * Changes the data store this view uses and refresh the view.
23459      * @param {Store} store
23460      */
23461     setStore : function(store, initial){
23462         if(!initial && this.store){
23463             this.store.un("datachanged", this.refresh);
23464             this.store.un("add", this.onAdd);
23465             this.store.un("remove", this.onRemove);
23466             this.store.un("update", this.onUpdate);
23467             this.store.un("clear", this.refresh);
23468         }
23469         if(store){
23470           
23471             store.on("datachanged", this.refresh, this);
23472             store.on("add", this.onAdd, this);
23473             store.on("remove", this.onRemove, this);
23474             store.on("update", this.onUpdate, this);
23475             store.on("clear", this.refresh, this);
23476         }
23477         
23478         if(store){
23479             this.refresh();
23480         }
23481     },
23482
23483     /**
23484      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23485      * @param {HTMLElement} node
23486      * @return {HTMLElement} The template node
23487      */
23488     findItemFromChild : function(node){
23489         var el = this.el.dom;
23490         if(!node || node.parentNode == el){
23491                     return node;
23492             }
23493             var p = node.parentNode;
23494             while(p && p != el){
23495             if(p.parentNode == el){
23496                 return p;
23497             }
23498             p = p.parentNode;
23499         }
23500             return null;
23501     },
23502
23503     /** @ignore */
23504     onClick : function(e){
23505         var item = this.findItemFromChild(e.getTarget());
23506         if(item){
23507             var index = this.indexOf(item);
23508             if(this.onItemClick(item, index, e) !== false){
23509                 this.fireEvent("click", this, index, item, e);
23510             }
23511         }else{
23512             this.clearSelections();
23513         }
23514     },
23515
23516     /** @ignore */
23517     onContextMenu : function(e){
23518         var item = this.findItemFromChild(e.getTarget());
23519         if(item){
23520             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23521         }
23522     },
23523
23524     /** @ignore */
23525     onDblClick : function(e){
23526         var item = this.findItemFromChild(e.getTarget());
23527         if(item){
23528             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23529         }
23530     },
23531
23532     onItemClick : function(item, index, e){
23533         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23534             return false;
23535         }
23536         if(this.multiSelect || this.singleSelect){
23537             if(this.multiSelect && e.shiftKey && this.lastSelection){
23538                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23539             }else{
23540                 this.select(item, this.multiSelect && e.ctrlKey);
23541                 this.lastSelection = item;
23542             }
23543             e.preventDefault();
23544         }
23545         return true;
23546     },
23547
23548     /**
23549      * Get the number of selected nodes.
23550      * @return {Number}
23551      */
23552     getSelectionCount : function(){
23553         return this.selections.length;
23554     },
23555
23556     /**
23557      * Get the currently selected nodes.
23558      * @return {Array} An array of HTMLElements
23559      */
23560     getSelectedNodes : function(){
23561         return this.selections;
23562     },
23563
23564     /**
23565      * Get the indexes of the selected nodes.
23566      * @return {Array}
23567      */
23568     getSelectedIndexes : function(){
23569         var indexes = [], s = this.selections;
23570         for(var i = 0, len = s.length; i < len; i++){
23571             indexes.push(s[i].nodeIndex);
23572         }
23573         return indexes;
23574     },
23575
23576     /**
23577      * Clear all selections
23578      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23579      */
23580     clearSelections : function(suppressEvent){
23581         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23582             this.cmp.elements = this.selections;
23583             this.cmp.removeClass(this.selectedClass);
23584             this.selections = [];
23585             if(!suppressEvent){
23586                 this.fireEvent("selectionchange", this, this.selections);
23587             }
23588         }
23589     },
23590
23591     /**
23592      * Returns true if the passed node is selected
23593      * @param {HTMLElement/Number} node The node or node index
23594      * @return {Boolean}
23595      */
23596     isSelected : function(node){
23597         var s = this.selections;
23598         if(s.length < 1){
23599             return false;
23600         }
23601         node = this.getNode(node);
23602         return s.indexOf(node) !== -1;
23603     },
23604
23605     /**
23606      * Selects nodes.
23607      * @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
23608      * @param {Boolean} keepExisting (optional) true to keep existing selections
23609      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23610      */
23611     select : function(nodeInfo, keepExisting, suppressEvent){
23612         if(nodeInfo instanceof Array){
23613             if(!keepExisting){
23614                 this.clearSelections(true);
23615             }
23616             for(var i = 0, len = nodeInfo.length; i < len; i++){
23617                 this.select(nodeInfo[i], true, true);
23618             }
23619         } else{
23620             var node = this.getNode(nodeInfo);
23621             if(node && !this.isSelected(node)){
23622                 if(!keepExisting){
23623                     this.clearSelections(true);
23624                 }
23625                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23626                     Roo.fly(node).addClass(this.selectedClass);
23627                     this.selections.push(node);
23628                     if(!suppressEvent){
23629                         this.fireEvent("selectionchange", this, this.selections);
23630                     }
23631                 }
23632             }
23633         }
23634     },
23635
23636     /**
23637      * Gets a template node.
23638      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23639      * @return {HTMLElement} The node or null if it wasn't found
23640      */
23641     getNode : function(nodeInfo){
23642         if(typeof nodeInfo == "string"){
23643             return document.getElementById(nodeInfo);
23644         }else if(typeof nodeInfo == "number"){
23645             return this.nodes[nodeInfo];
23646         }
23647         return nodeInfo;
23648     },
23649
23650     /**
23651      * Gets a range template nodes.
23652      * @param {Number} startIndex
23653      * @param {Number} endIndex
23654      * @return {Array} An array of nodes
23655      */
23656     getNodes : function(start, end){
23657         var ns = this.nodes;
23658         start = start || 0;
23659         end = typeof end == "undefined" ? ns.length - 1 : end;
23660         var nodes = [];
23661         if(start <= end){
23662             for(var i = start; i <= end; i++){
23663                 nodes.push(ns[i]);
23664             }
23665         } else{
23666             for(var i = start; i >= end; i--){
23667                 nodes.push(ns[i]);
23668             }
23669         }
23670         return nodes;
23671     },
23672
23673     /**
23674      * Finds the index of the passed node
23675      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23676      * @return {Number} The index of the node or -1
23677      */
23678     indexOf : function(node){
23679         node = this.getNode(node);
23680         if(typeof node.nodeIndex == "number"){
23681             return node.nodeIndex;
23682         }
23683         var ns = this.nodes;
23684         for(var i = 0, len = ns.length; i < len; i++){
23685             if(ns[i] == node){
23686                 return i;
23687             }
23688         }
23689         return -1;
23690     }
23691 });
23692 /*
23693  * Based on:
23694  * Ext JS Library 1.1.1
23695  * Copyright(c) 2006-2007, Ext JS, LLC.
23696  *
23697  * Originally Released Under LGPL - original licence link has changed is not relivant.
23698  *
23699  * Fork - LGPL
23700  * <script type="text/javascript">
23701  */
23702
23703 /**
23704  * @class Roo.JsonView
23705  * @extends Roo.View
23706  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23707 <pre><code>
23708 var view = new Roo.JsonView("my-element",
23709     '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23710     { multiSelect: true, jsonRoot: "data" }
23711 );
23712
23713 // listen for node click?
23714 view.on("click", function(vw, index, node, e){
23715     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23716 });
23717
23718 // direct load of JSON data
23719 view.load("foobar.php");
23720
23721 // Example from my blog list
23722 var tpl = new Roo.Template(
23723     '&lt;div class="entry"&gt;' +
23724     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23725     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23726     "&lt;/div&gt;&lt;hr /&gt;"
23727 );
23728
23729 var moreView = new Roo.JsonView("entry-list", tpl, {
23730     jsonRoot: "posts"
23731 });
23732 moreView.on("beforerender", this.sortEntries, this);
23733 moreView.load({
23734     url: "/blog/get-posts.php",
23735     params: "allposts=true",
23736     text: "Loading Blog Entries..."
23737 });
23738 </code></pre>
23739  * @constructor
23740  * Create a new JsonView
23741  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23742  * @param {Template} tpl The rendering template
23743  * @param {Object} config The config object
23744  */
23745 Roo.JsonView = function(container, tpl, config){
23746     Roo.JsonView.superclass.constructor.call(this, container, tpl, config);
23747
23748     var um = this.el.getUpdateManager();
23749     um.setRenderer(this);
23750     um.on("update", this.onLoad, this);
23751     um.on("failure", this.onLoadException, this);
23752
23753     /**
23754      * @event beforerender
23755      * Fires before rendering of the downloaded JSON data.
23756      * @param {Roo.JsonView} this
23757      * @param {Object} data The JSON data loaded
23758      */
23759     /**
23760      * @event load
23761      * Fires when data is loaded.
23762      * @param {Roo.JsonView} this
23763      * @param {Object} data The JSON data loaded
23764      * @param {Object} response The raw Connect response object
23765      */
23766     /**
23767      * @event loadexception
23768      * Fires when loading fails.
23769      * @param {Roo.JsonView} this
23770      * @param {Object} response The raw Connect response object
23771      */
23772     this.addEvents({
23773         'beforerender' : true,
23774         'load' : true,
23775         'loadexception' : true
23776     });
23777 };
23778 Roo.extend(Roo.JsonView, Roo.View, {
23779     /**
23780      * The root property in the loaded JSON object that contains the data
23781      * @type {String}
23782      */
23783     jsonRoot : "",
23784
23785     /**
23786      * Refreshes the view.
23787      */
23788     refresh : function(){
23789         this.clearSelections();
23790         this.el.update("");
23791         var html = [];
23792         var o = this.jsonData;
23793         if(o && o.length > 0){
23794             for(var i = 0, len = o.length; i < len; i++){
23795                 var data = this.prepareData(o[i], i, o);
23796                 html[html.length] = this.tpl.apply(data);
23797             }
23798         }else{
23799             html.push(this.emptyText);
23800         }
23801         this.el.update(html.join(""));
23802         this.nodes = this.el.dom.childNodes;
23803         this.updateIndexes(0);
23804     },
23805
23806     /**
23807      * 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.
23808      * @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:
23809      <pre><code>
23810      view.load({
23811          url: "your-url.php",
23812          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23813          callback: yourFunction,
23814          scope: yourObject, //(optional scope)
23815          discardUrl: false,
23816          nocache: false,
23817          text: "Loading...",
23818          timeout: 30,
23819          scripts: false
23820      });
23821      </code></pre>
23822      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23823      * 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.
23824      * @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}
23825      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23826      * @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.
23827      */
23828     load : function(){
23829         var um = this.el.getUpdateManager();
23830         um.update.apply(um, arguments);
23831     },
23832
23833     render : function(el, response){
23834         this.clearSelections();
23835         this.el.update("");
23836         var o;
23837         try{
23838             o = Roo.util.JSON.decode(response.responseText);
23839             if(this.jsonRoot){
23840                 
23841                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23842             }
23843         } catch(e){
23844         }
23845         /**
23846          * The current JSON data or null
23847          */
23848         this.jsonData = o;
23849         this.beforeRender();
23850         this.refresh();
23851     },
23852
23853 /**
23854  * Get the number of records in the current JSON dataset
23855  * @return {Number}
23856  */
23857     getCount : function(){
23858         return this.jsonData ? this.jsonData.length : 0;
23859     },
23860
23861 /**
23862  * Returns the JSON object for the specified node(s)
23863  * @param {HTMLElement/Array} node The node or an array of nodes
23864  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23865  * you get the JSON object for the node
23866  */
23867     getNodeData : function(node){
23868         if(node instanceof Array){
23869             var data = [];
23870             for(var i = 0, len = node.length; i < len; i++){
23871                 data.push(this.getNodeData(node[i]));
23872             }
23873             return data;
23874         }
23875         return this.jsonData[this.indexOf(node)] || null;
23876     },
23877
23878     beforeRender : function(){
23879         this.snapshot = this.jsonData;
23880         if(this.sortInfo){
23881             this.sort.apply(this, this.sortInfo);
23882         }
23883         this.fireEvent("beforerender", this, this.jsonData);
23884     },
23885
23886     onLoad : function(el, o){
23887         this.fireEvent("load", this, this.jsonData, o);
23888     },
23889
23890     onLoadException : function(el, o){
23891         this.fireEvent("loadexception", this, o);
23892     },
23893
23894 /**
23895  * Filter the data by a specific property.
23896  * @param {String} property A property on your JSON objects
23897  * @param {String/RegExp} value Either string that the property values
23898  * should start with, or a RegExp to test against the property
23899  */
23900     filter : function(property, value){
23901         if(this.jsonData){
23902             var data = [];
23903             var ss = this.snapshot;
23904             if(typeof value == "string"){
23905                 var vlen = value.length;
23906                 if(vlen == 0){
23907                     this.clearFilter();
23908                     return;
23909                 }
23910                 value = value.toLowerCase();
23911                 for(var i = 0, len = ss.length; i < len; i++){
23912                     var o = ss[i];
23913                     if(o[property].substr(0, vlen).toLowerCase() == value){
23914                         data.push(o);
23915                     }
23916                 }
23917             } else if(value.exec){ // regex?
23918                 for(var i = 0, len = ss.length; i < len; i++){
23919                     var o = ss[i];
23920                     if(value.test(o[property])){
23921                         data.push(o);
23922                     }
23923                 }
23924             } else{
23925                 return;
23926             }
23927             this.jsonData = data;
23928             this.refresh();
23929         }
23930     },
23931
23932 /**
23933  * Filter by a function. The passed function will be called with each
23934  * object in the current dataset. If the function returns true the value is kept,
23935  * otherwise it is filtered.
23936  * @param {Function} fn
23937  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23938  */
23939     filterBy : function(fn, scope){
23940         if(this.jsonData){
23941             var data = [];
23942             var ss = this.snapshot;
23943             for(var i = 0, len = ss.length; i < len; i++){
23944                 var o = ss[i];
23945                 if(fn.call(scope || this, o)){
23946                     data.push(o);
23947                 }
23948             }
23949             this.jsonData = data;
23950             this.refresh();
23951         }
23952     },
23953
23954 /**
23955  * Clears the current filter.
23956  */
23957     clearFilter : function(){
23958         if(this.snapshot && this.jsonData != this.snapshot){
23959             this.jsonData = this.snapshot;
23960             this.refresh();
23961         }
23962     },
23963
23964
23965 /**
23966  * Sorts the data for this view and refreshes it.
23967  * @param {String} property A property on your JSON objects to sort on
23968  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
23969  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
23970  */
23971     sort : function(property, dir, sortType){
23972         this.sortInfo = Array.prototype.slice.call(arguments, 0);
23973         if(this.jsonData){
23974             var p = property;
23975             var dsc = dir && dir.toLowerCase() == "desc";
23976             var f = function(o1, o2){
23977                 var v1 = sortType ? sortType(o1[p]) : o1[p];
23978                 var v2 = sortType ? sortType(o2[p]) : o2[p];
23979                 ;
23980                 if(v1 < v2){
23981                     return dsc ? +1 : -1;
23982                 } else if(v1 > v2){
23983                     return dsc ? -1 : +1;
23984                 } else{
23985                     return 0;
23986                 }
23987             };
23988             this.jsonData.sort(f);
23989             this.refresh();
23990             if(this.jsonData != this.snapshot){
23991                 this.snapshot.sort(f);
23992             }
23993         }
23994     }
23995 });/*
23996  * Based on:
23997  * Ext JS Library 1.1.1
23998  * Copyright(c) 2006-2007, Ext JS, LLC.
23999  *
24000  * Originally Released Under LGPL - original licence link has changed is not relivant.
24001  *
24002  * Fork - LGPL
24003  * <script type="text/javascript">
24004  */
24005  
24006
24007 /**
24008  * @class Roo.ColorPalette
24009  * @extends Roo.Component
24010  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24011  * Here's an example of typical usage:
24012  * <pre><code>
24013 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24014 cp.render('my-div');
24015
24016 cp.on('select', function(palette, selColor){
24017     // do something with selColor
24018 });
24019 </code></pre>
24020  * @constructor
24021  * Create a new ColorPalette
24022  * @param {Object} config The config object
24023  */
24024 Roo.ColorPalette = function(config){
24025     Roo.ColorPalette.superclass.constructor.call(this, config);
24026     this.addEvents({
24027         /**
24028              * @event select
24029              * Fires when a color is selected
24030              * @param {ColorPalette} this
24031              * @param {String} color The 6-digit color hex code (without the # symbol)
24032              */
24033         select: true
24034     });
24035
24036     if(this.handler){
24037         this.on("select", this.handler, this.scope, true);
24038     }
24039 };
24040 Roo.extend(Roo.ColorPalette, Roo.Component, {
24041     /**
24042      * @cfg {String} itemCls
24043      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24044      */
24045     itemCls : "x-color-palette",
24046     /**
24047      * @cfg {String} value
24048      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24049      * the hex codes are case-sensitive.
24050      */
24051     value : null,
24052     clickEvent:'click',
24053     // private
24054     ctype: "Roo.ColorPalette",
24055
24056     /**
24057      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24058      */
24059     allowReselect : false,
24060
24061     /**
24062      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24063      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24064      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24065      * of colors with the width setting until the box is symmetrical.</p>
24066      * <p>You can override individual colors if needed:</p>
24067      * <pre><code>
24068 var cp = new Roo.ColorPalette();
24069 cp.colors[0] = "FF0000";  // change the first box to red
24070 </code></pre>
24071
24072 Or you can provide a custom array of your own for complete control:
24073 <pre><code>
24074 var cp = new Roo.ColorPalette();
24075 cp.colors = ["000000", "993300", "333300"];
24076 </code></pre>
24077      * @type Array
24078      */
24079     colors : [
24080         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24081         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24082         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24083         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24084         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24085     ],
24086
24087     // private
24088     onRender : function(container, position){
24089         var t = new Roo.MasterTemplate(
24090             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24091         );
24092         var c = this.colors;
24093         for(var i = 0, len = c.length; i < len; i++){
24094             t.add([c[i]]);
24095         }
24096         var el = document.createElement("div");
24097         el.className = this.itemCls;
24098         t.overwrite(el);
24099         container.dom.insertBefore(el, position);
24100         this.el = Roo.get(el);
24101         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24102         if(this.clickEvent != 'click'){
24103             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24104         }
24105     },
24106
24107     // private
24108     afterRender : function(){
24109         Roo.ColorPalette.superclass.afterRender.call(this);
24110         if(this.value){
24111             var s = this.value;
24112             this.value = null;
24113             this.select(s);
24114         }
24115     },
24116
24117     // private
24118     handleClick : function(e, t){
24119         e.preventDefault();
24120         if(!this.disabled){
24121             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24122             this.select(c.toUpperCase());
24123         }
24124     },
24125
24126     /**
24127      * Selects the specified color in the palette (fires the select event)
24128      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24129      */
24130     select : function(color){
24131         color = color.replace("#", "");
24132         if(color != this.value || this.allowReselect){
24133             var el = this.el;
24134             if(this.value){
24135                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24136             }
24137             el.child("a.color-"+color).addClass("x-color-palette-sel");
24138             this.value = color;
24139             this.fireEvent("select", this, color);
24140         }
24141     }
24142 });/*
24143  * Based on:
24144  * Ext JS Library 1.1.1
24145  * Copyright(c) 2006-2007, Ext JS, LLC.
24146  *
24147  * Originally Released Under LGPL - original licence link has changed is not relivant.
24148  *
24149  * Fork - LGPL
24150  * <script type="text/javascript">
24151  */
24152  
24153 /**
24154  * @class Roo.DatePicker
24155  * @extends Roo.Component
24156  * Simple date picker class.
24157  * @constructor
24158  * Create a new DatePicker
24159  * @param {Object} config The config object
24160  */
24161 Roo.DatePicker = function(config){
24162     Roo.DatePicker.superclass.constructor.call(this, config);
24163
24164     this.value = config && config.value ?
24165                  config.value.clearTime() : new Date().clearTime();
24166
24167     this.addEvents({
24168         /**
24169              * @event select
24170              * Fires when a date is selected
24171              * @param {DatePicker} this
24172              * @param {Date} date The selected date
24173              */
24174         select: true
24175     });
24176
24177     if(this.handler){
24178         this.on("select", this.handler,  this.scope || this);
24179     }
24180     // build the disabledDatesRE
24181     if(!this.disabledDatesRE && this.disabledDates){
24182         var dd = this.disabledDates;
24183         var re = "(?:";
24184         for(var i = 0; i < dd.length; i++){
24185             re += dd[i];
24186             if(i != dd.length-1) re += "|";
24187         }
24188         this.disabledDatesRE = new RegExp(re + ")");
24189     }
24190 };
24191
24192 Roo.extend(Roo.DatePicker, Roo.Component, {
24193     /**
24194      * @cfg {String} todayText
24195      * The text to display on the button that selects the current date (defaults to "Today")
24196      */
24197     todayText : "Today",
24198     /**
24199      * @cfg {String} okText
24200      * The text to display on the ok button
24201      */
24202     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24203     /**
24204      * @cfg {String} cancelText
24205      * The text to display on the cancel button
24206      */
24207     cancelText : "Cancel",
24208     /**
24209      * @cfg {String} todayTip
24210      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24211      */
24212     todayTip : "{0} (Spacebar)",
24213     /**
24214      * @cfg {Date} minDate
24215      * Minimum allowable date (JavaScript date object, defaults to null)
24216      */
24217     minDate : null,
24218     /**
24219      * @cfg {Date} maxDate
24220      * Maximum allowable date (JavaScript date object, defaults to null)
24221      */
24222     maxDate : null,
24223     /**
24224      * @cfg {String} minText
24225      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24226      */
24227     minText : "This date is before the minimum date",
24228     /**
24229      * @cfg {String} maxText
24230      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24231      */
24232     maxText : "This date is after the maximum date",
24233     /**
24234      * @cfg {String} format
24235      * The default date format string which can be overriden for localization support.  The format must be
24236      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24237      */
24238     format : "m/d/y",
24239     /**
24240      * @cfg {Array} disabledDays
24241      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24242      */
24243     disabledDays : null,
24244     /**
24245      * @cfg {String} disabledDaysText
24246      * The tooltip to display when the date falls on a disabled day (defaults to "")
24247      */
24248     disabledDaysText : "",
24249     /**
24250      * @cfg {RegExp} disabledDatesRE
24251      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24252      */
24253     disabledDatesRE : null,
24254     /**
24255      * @cfg {String} disabledDatesText
24256      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24257      */
24258     disabledDatesText : "",
24259     /**
24260      * @cfg {Boolean} constrainToViewport
24261      * True to constrain the date picker to the viewport (defaults to true)
24262      */
24263     constrainToViewport : true,
24264     /**
24265      * @cfg {Array} monthNames
24266      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24267      */
24268     monthNames : Date.monthNames,
24269     /**
24270      * @cfg {Array} dayNames
24271      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24272      */
24273     dayNames : Date.dayNames,
24274     /**
24275      * @cfg {String} nextText
24276      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24277      */
24278     nextText: 'Next Month (Control+Right)',
24279     /**
24280      * @cfg {String} prevText
24281      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24282      */
24283     prevText: 'Previous Month (Control+Left)',
24284     /**
24285      * @cfg {String} monthYearText
24286      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24287      */
24288     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24289     /**
24290      * @cfg {Number} startDay
24291      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24292      */
24293     startDay : 0,
24294     /**
24295      * @cfg {Bool} showClear
24296      * Show a clear button (usefull for date form elements that can be blank.)
24297      */
24298     
24299     showClear: false,
24300     
24301     /**
24302      * Sets the value of the date field
24303      * @param {Date} value The date to set
24304      */
24305     setValue : function(value){
24306         var old = this.value;
24307         this.value = value.clearTime(true);
24308         if(this.el){
24309             this.update(this.value);
24310         }
24311     },
24312
24313     /**
24314      * Gets the current selected value of the date field
24315      * @return {Date} The selected date
24316      */
24317     getValue : function(){
24318         return this.value;
24319     },
24320
24321     // private
24322     focus : function(){
24323         if(this.el){
24324             this.update(this.activeDate);
24325         }
24326     },
24327
24328     // private
24329     onRender : function(container, position){
24330         var m = [
24331              '<table cellspacing="0">',
24332                 '<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>',
24333                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24334         var dn = this.dayNames;
24335         for(var i = 0; i < 7; i++){
24336             var d = this.startDay+i;
24337             if(d > 6){
24338                 d = d-7;
24339             }
24340             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24341         }
24342         m[m.length] = "</tr></thead><tbody><tr>";
24343         for(var i = 0; i < 42; i++) {
24344             if(i % 7 == 0 && i != 0){
24345                 m[m.length] = "</tr><tr>";
24346             }
24347             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24348         }
24349         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24350             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24351
24352         var el = document.createElement("div");
24353         el.className = "x-date-picker";
24354         el.innerHTML = m.join("");
24355
24356         container.dom.insertBefore(el, position);
24357
24358         this.el = Roo.get(el);
24359         this.eventEl = Roo.get(el.firstChild);
24360
24361         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24362             handler: this.showPrevMonth,
24363             scope: this,
24364             preventDefault:true,
24365             stopDefault:true
24366         });
24367
24368         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24369             handler: this.showNextMonth,
24370             scope: this,
24371             preventDefault:true,
24372             stopDefault:true
24373         });
24374
24375         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24376
24377         this.monthPicker = this.el.down('div.x-date-mp');
24378         this.monthPicker.enableDisplayMode('block');
24379         
24380         var kn = new Roo.KeyNav(this.eventEl, {
24381             "left" : function(e){
24382                 e.ctrlKey ?
24383                     this.showPrevMonth() :
24384                     this.update(this.activeDate.add("d", -1));
24385             },
24386
24387             "right" : function(e){
24388                 e.ctrlKey ?
24389                     this.showNextMonth() :
24390                     this.update(this.activeDate.add("d", 1));
24391             },
24392
24393             "up" : function(e){
24394                 e.ctrlKey ?
24395                     this.showNextYear() :
24396                     this.update(this.activeDate.add("d", -7));
24397             },
24398
24399             "down" : function(e){
24400                 e.ctrlKey ?
24401                     this.showPrevYear() :
24402                     this.update(this.activeDate.add("d", 7));
24403             },
24404
24405             "pageUp" : function(e){
24406                 this.showNextMonth();
24407             },
24408
24409             "pageDown" : function(e){
24410                 this.showPrevMonth();
24411             },
24412
24413             "enter" : function(e){
24414                 e.stopPropagation();
24415                 return true;
24416             },
24417
24418             scope : this
24419         });
24420
24421         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24422
24423         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24424
24425         this.el.unselectable();
24426         
24427         this.cells = this.el.select("table.x-date-inner tbody td");
24428         this.textNodes = this.el.query("table.x-date-inner tbody span");
24429
24430         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24431             text: "&#160;",
24432             tooltip: this.monthYearText
24433         });
24434
24435         this.mbtn.on('click', this.showMonthPicker, this);
24436         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24437
24438
24439         var today = (new Date()).dateFormat(this.format);
24440         
24441         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24442         baseTb.add({
24443             text: String.format(this.todayText, today),
24444             tooltip: String.format(this.todayTip, today),
24445             handler: this.selectToday,
24446             scope: this
24447         });
24448         
24449         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24450             
24451         //});
24452         if (this.showClear) {
24453             
24454             baseTb.add( new Roo.Toolbar.Fill());
24455             baseTb.add({
24456                 text: '&#160;',
24457                 cls: 'x-btn-icon x-btn-clear',
24458                 handler: function() {
24459                     //this.value = '';
24460                     this.fireEvent("select", this, '');
24461                 },
24462                 scope: this
24463             });
24464         }
24465         
24466         
24467         if(Roo.isIE){
24468             this.el.repaint();
24469         }
24470         this.update(this.value);
24471     },
24472
24473     createMonthPicker : function(){
24474         if(!this.monthPicker.dom.firstChild){
24475             var buf = ['<table border="0" cellspacing="0">'];
24476             for(var i = 0; i < 6; i++){
24477                 buf.push(
24478                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24479                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24480                     i == 0 ?
24481                     '<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>' :
24482                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24483                 );
24484             }
24485             buf.push(
24486                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24487                     this.okText,
24488                     '</button><button type="button" class="x-date-mp-cancel">',
24489                     this.cancelText,
24490                     '</button></td></tr>',
24491                 '</table>'
24492             );
24493             this.monthPicker.update(buf.join(''));
24494             this.monthPicker.on('click', this.onMonthClick, this);
24495             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24496
24497             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24498             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24499
24500             this.mpMonths.each(function(m, a, i){
24501                 i += 1;
24502                 if((i%2) == 0){
24503                     m.dom.xmonth = 5 + Math.round(i * .5);
24504                 }else{
24505                     m.dom.xmonth = Math.round((i-1) * .5);
24506                 }
24507             });
24508         }
24509     },
24510
24511     showMonthPicker : function(){
24512         this.createMonthPicker();
24513         var size = this.el.getSize();
24514         this.monthPicker.setSize(size);
24515         this.monthPicker.child('table').setSize(size);
24516
24517         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24518         this.updateMPMonth(this.mpSelMonth);
24519         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24520         this.updateMPYear(this.mpSelYear);
24521
24522         this.monthPicker.slideIn('t', {duration:.2});
24523     },
24524
24525     updateMPYear : function(y){
24526         this.mpyear = y;
24527         var ys = this.mpYears.elements;
24528         for(var i = 1; i <= 10; i++){
24529             var td = ys[i-1], y2;
24530             if((i%2) == 0){
24531                 y2 = y + Math.round(i * .5);
24532                 td.firstChild.innerHTML = y2;
24533                 td.xyear = y2;
24534             }else{
24535                 y2 = y - (5-Math.round(i * .5));
24536                 td.firstChild.innerHTML = y2;
24537                 td.xyear = y2;
24538             }
24539             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24540         }
24541     },
24542
24543     updateMPMonth : function(sm){
24544         this.mpMonths.each(function(m, a, i){
24545             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24546         });
24547     },
24548
24549     selectMPMonth: function(m){
24550         
24551     },
24552
24553     onMonthClick : function(e, t){
24554         e.stopEvent();
24555         var el = new Roo.Element(t), pn;
24556         if(el.is('button.x-date-mp-cancel')){
24557             this.hideMonthPicker();
24558         }
24559         else if(el.is('button.x-date-mp-ok')){
24560             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24561             this.hideMonthPicker();
24562         }
24563         else if(pn = el.up('td.x-date-mp-month', 2)){
24564             this.mpMonths.removeClass('x-date-mp-sel');
24565             pn.addClass('x-date-mp-sel');
24566             this.mpSelMonth = pn.dom.xmonth;
24567         }
24568         else if(pn = el.up('td.x-date-mp-year', 2)){
24569             this.mpYears.removeClass('x-date-mp-sel');
24570             pn.addClass('x-date-mp-sel');
24571             this.mpSelYear = pn.dom.xyear;
24572         }
24573         else if(el.is('a.x-date-mp-prev')){
24574             this.updateMPYear(this.mpyear-10);
24575         }
24576         else if(el.is('a.x-date-mp-next')){
24577             this.updateMPYear(this.mpyear+10);
24578         }
24579     },
24580
24581     onMonthDblClick : function(e, t){
24582         e.stopEvent();
24583         var el = new Roo.Element(t), pn;
24584         if(pn = el.up('td.x-date-mp-month', 2)){
24585             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24586             this.hideMonthPicker();
24587         }
24588         else if(pn = el.up('td.x-date-mp-year', 2)){
24589             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24590             this.hideMonthPicker();
24591         }
24592     },
24593
24594     hideMonthPicker : function(disableAnim){
24595         if(this.monthPicker){
24596             if(disableAnim === true){
24597                 this.monthPicker.hide();
24598             }else{
24599                 this.monthPicker.slideOut('t', {duration:.2});
24600             }
24601         }
24602     },
24603
24604     // private
24605     showPrevMonth : function(e){
24606         this.update(this.activeDate.add("mo", -1));
24607     },
24608
24609     // private
24610     showNextMonth : function(e){
24611         this.update(this.activeDate.add("mo", 1));
24612     },
24613
24614     // private
24615     showPrevYear : function(){
24616         this.update(this.activeDate.add("y", -1));
24617     },
24618
24619     // private
24620     showNextYear : function(){
24621         this.update(this.activeDate.add("y", 1));
24622     },
24623
24624     // private
24625     handleMouseWheel : function(e){
24626         var delta = e.getWheelDelta();
24627         if(delta > 0){
24628             this.showPrevMonth();
24629             e.stopEvent();
24630         } else if(delta < 0){
24631             this.showNextMonth();
24632             e.stopEvent();
24633         }
24634     },
24635
24636     // private
24637     handleDateClick : function(e, t){
24638         e.stopEvent();
24639         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24640             this.setValue(new Date(t.dateValue));
24641             this.fireEvent("select", this, this.value);
24642         }
24643     },
24644
24645     // private
24646     selectToday : function(){
24647         this.setValue(new Date().clearTime());
24648         this.fireEvent("select", this, this.value);
24649     },
24650
24651     // private
24652     update : function(date){
24653         var vd = this.activeDate;
24654         this.activeDate = date;
24655         if(vd && this.el){
24656             var t = date.getTime();
24657             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24658                 this.cells.removeClass("x-date-selected");
24659                 this.cells.each(function(c){
24660                    if(c.dom.firstChild.dateValue == t){
24661                        c.addClass("x-date-selected");
24662                        setTimeout(function(){
24663                             try{c.dom.firstChild.focus();}catch(e){}
24664                        }, 50);
24665                        return false;
24666                    }
24667                 });
24668                 return;
24669             }
24670         }
24671         var days = date.getDaysInMonth();
24672         var firstOfMonth = date.getFirstDateOfMonth();
24673         var startingPos = firstOfMonth.getDay()-this.startDay;
24674
24675         if(startingPos <= this.startDay){
24676             startingPos += 7;
24677         }
24678
24679         var pm = date.add("mo", -1);
24680         var prevStart = pm.getDaysInMonth()-startingPos;
24681
24682         var cells = this.cells.elements;
24683         var textEls = this.textNodes;
24684         days += startingPos;
24685
24686         // convert everything to numbers so it's fast
24687         var day = 86400000;
24688         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24689         var today = new Date().clearTime().getTime();
24690         var sel = date.clearTime().getTime();
24691         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24692         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24693         var ddMatch = this.disabledDatesRE;
24694         var ddText = this.disabledDatesText;
24695         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24696         var ddaysText = this.disabledDaysText;
24697         var format = this.format;
24698
24699         var setCellClass = function(cal, cell){
24700             cell.title = "";
24701             var t = d.getTime();
24702             cell.firstChild.dateValue = t;
24703             if(t == today){
24704                 cell.className += " x-date-today";
24705                 cell.title = cal.todayText;
24706             }
24707             if(t == sel){
24708                 cell.className += " x-date-selected";
24709                 setTimeout(function(){
24710                     try{cell.firstChild.focus();}catch(e){}
24711                 }, 50);
24712             }
24713             // disabling
24714             if(t < min) {
24715                 cell.className = " x-date-disabled";
24716                 cell.title = cal.minText;
24717                 return;
24718             }
24719             if(t > max) {
24720                 cell.className = " x-date-disabled";
24721                 cell.title = cal.maxText;
24722                 return;
24723             }
24724             if(ddays){
24725                 if(ddays.indexOf(d.getDay()) != -1){
24726                     cell.title = ddaysText;
24727                     cell.className = " x-date-disabled";
24728                 }
24729             }
24730             if(ddMatch && format){
24731                 var fvalue = d.dateFormat(format);
24732                 if(ddMatch.test(fvalue)){
24733                     cell.title = ddText.replace("%0", fvalue);
24734                     cell.className = " x-date-disabled";
24735                 }
24736             }
24737         };
24738
24739         var i = 0;
24740         for(; i < startingPos; i++) {
24741             textEls[i].innerHTML = (++prevStart);
24742             d.setDate(d.getDate()+1);
24743             cells[i].className = "x-date-prevday";
24744             setCellClass(this, cells[i]);
24745         }
24746         for(; i < days; i++){
24747             intDay = i - startingPos + 1;
24748             textEls[i].innerHTML = (intDay);
24749             d.setDate(d.getDate()+1);
24750             cells[i].className = "x-date-active";
24751             setCellClass(this, cells[i]);
24752         }
24753         var extraDays = 0;
24754         for(; i < 42; i++) {
24755              textEls[i].innerHTML = (++extraDays);
24756              d.setDate(d.getDate()+1);
24757              cells[i].className = "x-date-nextday";
24758              setCellClass(this, cells[i]);
24759         }
24760
24761         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24762
24763         if(!this.internalRender){
24764             var main = this.el.dom.firstChild;
24765             var w = main.offsetWidth;
24766             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24767             Roo.fly(main).setWidth(w);
24768             this.internalRender = true;
24769             // opera does not respect the auto grow header center column
24770             // then, after it gets a width opera refuses to recalculate
24771             // without a second pass
24772             if(Roo.isOpera && !this.secondPass){
24773                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24774                 this.secondPass = true;
24775                 this.update.defer(10, this, [date]);
24776             }
24777         }
24778     }
24779 });/*
24780  * Based on:
24781  * Ext JS Library 1.1.1
24782  * Copyright(c) 2006-2007, Ext JS, LLC.
24783  *
24784  * Originally Released Under LGPL - original licence link has changed is not relivant.
24785  *
24786  * Fork - LGPL
24787  * <script type="text/javascript">
24788  */
24789 /**
24790  * @class Roo.TabPanel
24791  * @extends Roo.util.Observable
24792  * A lightweight tab container.
24793  * <br><br>
24794  * Usage:
24795  * <pre><code>
24796 // basic tabs 1, built from existing content
24797 var tabs = new Roo.TabPanel("tabs1");
24798 tabs.addTab("script", "View Script");
24799 tabs.addTab("markup", "View Markup");
24800 tabs.activate("script");
24801
24802 // more advanced tabs, built from javascript
24803 var jtabs = new Roo.TabPanel("jtabs");
24804 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24805
24806 // set up the UpdateManager
24807 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24808 var updater = tab2.getUpdateManager();
24809 updater.setDefaultUrl("ajax1.htm");
24810 tab2.on('activate', updater.refresh, updater, true);
24811
24812 // Use setUrl for Ajax loading
24813 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24814 tab3.setUrl("ajax2.htm", null, true);
24815
24816 // Disabled tab
24817 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24818 tab4.disable();
24819
24820 jtabs.activate("jtabs-1");
24821  * </code></pre>
24822  * @constructor
24823  * Create a new TabPanel.
24824  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24825  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24826  */
24827 Roo.TabPanel = function(container, config){
24828     /**
24829     * The container element for this TabPanel.
24830     * @type Roo.Element
24831     */
24832     this.el = Roo.get(container, true);
24833     if(config){
24834         if(typeof config == "boolean"){
24835             this.tabPosition = config ? "bottom" : "top";
24836         }else{
24837             Roo.apply(this, config);
24838         }
24839     }
24840     if(this.tabPosition == "bottom"){
24841         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24842         this.el.addClass("x-tabs-bottom");
24843     }
24844     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24845     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24846     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24847     if(Roo.isIE){
24848         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24849     }
24850     if(this.tabPosition != "bottom"){
24851     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24852      * @type Roo.Element
24853      */
24854       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24855       this.el.addClass("x-tabs-top");
24856     }
24857     this.items = [];
24858
24859     this.bodyEl.setStyle("position", "relative");
24860
24861     this.active = null;
24862     this.activateDelegate = this.activate.createDelegate(this);
24863
24864     this.addEvents({
24865         /**
24866          * @event tabchange
24867          * Fires when the active tab changes
24868          * @param {Roo.TabPanel} this
24869          * @param {Roo.TabPanelItem} activePanel The new active tab
24870          */
24871         "tabchange": true,
24872         /**
24873          * @event beforetabchange
24874          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24875          * @param {Roo.TabPanel} this
24876          * @param {Object} e Set cancel to true on this object to cancel the tab change
24877          * @param {Roo.TabPanelItem} tab The tab being changed to
24878          */
24879         "beforetabchange" : true
24880     });
24881
24882     Roo.EventManager.onWindowResize(this.onResize, this);
24883     this.cpad = this.el.getPadding("lr");
24884     this.hiddenCount = 0;
24885
24886     Roo.TabPanel.superclass.constructor.call(this);
24887 };
24888
24889 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24890         /*
24891          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24892          */
24893     tabPosition : "top",
24894         /*
24895          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24896          */
24897     currentTabWidth : 0,
24898         /*
24899          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24900          */
24901     minTabWidth : 40,
24902         /*
24903          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24904          */
24905     maxTabWidth : 250,
24906         /*
24907          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24908          */
24909     preferredTabWidth : 175,
24910         /*
24911          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24912          */
24913     resizeTabs : false,
24914         /*
24915          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24916          */
24917     monitorResize : true,
24918
24919     /**
24920      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24921      * @param {String} id The id of the div to use <b>or create</b>
24922      * @param {String} text The text for the tab
24923      * @param {String} content (optional) Content to put in the TabPanelItem body
24924      * @param {Boolean} closable (optional) True to create a close icon on the tab
24925      * @return {Roo.TabPanelItem} The created TabPanelItem
24926      */
24927     addTab : function(id, text, content, closable){
24928         var item = new Roo.TabPanelItem(this, id, text, closable);
24929         this.addTabItem(item);
24930         if(content){
24931             item.setContent(content);
24932         }
24933         return item;
24934     },
24935
24936     /**
24937      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24938      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24939      * @return {Roo.TabPanelItem}
24940      */
24941     getTab : function(id){
24942         return this.items[id];
24943     },
24944
24945     /**
24946      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24947      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24948      */
24949     hideTab : function(id){
24950         var t = this.items[id];
24951         if(!t.isHidden()){
24952            t.setHidden(true);
24953            this.hiddenCount++;
24954            this.autoSizeTabs();
24955         }
24956     },
24957
24958     /**
24959      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
24960      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
24961      */
24962     unhideTab : function(id){
24963         var t = this.items[id];
24964         if(t.isHidden()){
24965            t.setHidden(false);
24966            this.hiddenCount--;
24967            this.autoSizeTabs();
24968         }
24969     },
24970
24971     /**
24972      * Adds an existing {@link Roo.TabPanelItem}.
24973      * @param {Roo.TabPanelItem} item The TabPanelItem to add
24974      */
24975     addTabItem : function(item){
24976         this.items[item.id] = item;
24977         this.items.push(item);
24978         if(this.resizeTabs){
24979            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
24980            this.autoSizeTabs();
24981         }else{
24982             item.autoSize();
24983         }
24984     },
24985
24986     /**
24987      * Removes a {@link Roo.TabPanelItem}.
24988      * @param {String/Number} id The id or index of the TabPanelItem to remove.
24989      */
24990     removeTab : function(id){
24991         var items = this.items;
24992         var tab = items[id];
24993         if(!tab) return;
24994         var index = items.indexOf(tab);
24995         if(this.active == tab && items.length > 1){
24996             var newTab = this.getNextAvailable(index);
24997             if(newTab)newTab.activate();
24998         }
24999         this.stripEl.dom.removeChild(tab.pnode.dom);
25000         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25001             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25002         }
25003         items.splice(index, 1);
25004         delete this.items[tab.id];
25005         tab.fireEvent("close", tab);
25006         tab.purgeListeners();
25007         this.autoSizeTabs();
25008     },
25009
25010     getNextAvailable : function(start){
25011         var items = this.items;
25012         var index = start;
25013         // look for a next tab that will slide over to
25014         // replace the one being removed
25015         while(index < items.length){
25016             var item = items[++index];
25017             if(item && !item.isHidden()){
25018                 return item;
25019             }
25020         }
25021         // if one isn't found select the previous tab (on the left)
25022         index = start;
25023         while(index >= 0){
25024             var item = items[--index];
25025             if(item && !item.isHidden()){
25026                 return item;
25027             }
25028         }
25029         return null;
25030     },
25031
25032     /**
25033      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25034      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25035      */
25036     disableTab : function(id){
25037         var tab = this.items[id];
25038         if(tab && this.active != tab){
25039             tab.disable();
25040         }
25041     },
25042
25043     /**
25044      * Enables a {@link Roo.TabPanelItem} that is disabled.
25045      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25046      */
25047     enableTab : function(id){
25048         var tab = this.items[id];
25049         tab.enable();
25050     },
25051
25052     /**
25053      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25054      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25055      * @return {Roo.TabPanelItem} The TabPanelItem.
25056      */
25057     activate : function(id){
25058         var tab = this.items[id];
25059         if(!tab){
25060             return null;
25061         }
25062         if(tab == this.active || tab.disabled){
25063             return tab;
25064         }
25065         var e = {};
25066         this.fireEvent("beforetabchange", this, e, tab);
25067         if(e.cancel !== true && !tab.disabled){
25068             if(this.active){
25069                 this.active.hide();
25070             }
25071             this.active = this.items[id];
25072             this.active.show();
25073             this.fireEvent("tabchange", this, this.active);
25074         }
25075         return tab;
25076     },
25077
25078     /**
25079      * Gets the active {@link Roo.TabPanelItem}.
25080      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25081      */
25082     getActiveTab : function(){
25083         return this.active;
25084     },
25085
25086     /**
25087      * Updates the tab body element to fit the height of the container element
25088      * for overflow scrolling
25089      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25090      */
25091     syncHeight : function(targetHeight){
25092         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25093         var bm = this.bodyEl.getMargins();
25094         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25095         this.bodyEl.setHeight(newHeight);
25096         return newHeight;
25097     },
25098
25099     onResize : function(){
25100         if(this.monitorResize){
25101             this.autoSizeTabs();
25102         }
25103     },
25104
25105     /**
25106      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25107      */
25108     beginUpdate : function(){
25109         this.updating = true;
25110     },
25111
25112     /**
25113      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25114      */
25115     endUpdate : function(){
25116         this.updating = false;
25117         this.autoSizeTabs();
25118     },
25119
25120     /**
25121      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25122      */
25123     autoSizeTabs : function(){
25124         var count = this.items.length;
25125         var vcount = count - this.hiddenCount;
25126         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25127         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25128         var availWidth = Math.floor(w / vcount);
25129         var b = this.stripBody;
25130         if(b.getWidth() > w){
25131             var tabs = this.items;
25132             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25133             if(availWidth < this.minTabWidth){
25134                 /*if(!this.sleft){    // incomplete scrolling code
25135                     this.createScrollButtons();
25136                 }
25137                 this.showScroll();
25138                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25139             }
25140         }else{
25141             if(this.currentTabWidth < this.preferredTabWidth){
25142                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25143             }
25144         }
25145     },
25146
25147     /**
25148      * Returns the number of tabs in this TabPanel.
25149      * @return {Number}
25150      */
25151      getCount : function(){
25152          return this.items.length;
25153      },
25154
25155     /**
25156      * Resizes all the tabs to the passed width
25157      * @param {Number} The new width
25158      */
25159     setTabWidth : function(width){
25160         this.currentTabWidth = width;
25161         for(var i = 0, len = this.items.length; i < len; i++) {
25162                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25163         }
25164     },
25165
25166     /**
25167      * Destroys this TabPanel
25168      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25169      */
25170     destroy : function(removeEl){
25171         Roo.EventManager.removeResizeListener(this.onResize, this);
25172         for(var i = 0, len = this.items.length; i < len; i++){
25173             this.items[i].purgeListeners();
25174         }
25175         if(removeEl === true){
25176             this.el.update("");
25177             this.el.remove();
25178         }
25179     }
25180 });
25181
25182 /**
25183  * @class Roo.TabPanelItem
25184  * @extends Roo.util.Observable
25185  * Represents an individual item (tab plus body) in a TabPanel.
25186  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25187  * @param {String} id The id of this TabPanelItem
25188  * @param {String} text The text for the tab of this TabPanelItem
25189  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25190  */
25191 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25192     /**
25193      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25194      * @type Roo.TabPanel
25195      */
25196     this.tabPanel = tabPanel;
25197     /**
25198      * The id for this TabPanelItem
25199      * @type String
25200      */
25201     this.id = id;
25202     /** @private */
25203     this.disabled = false;
25204     /** @private */
25205     this.text = text;
25206     /** @private */
25207     this.loaded = false;
25208     this.closable = closable;
25209
25210     /**
25211      * The body element for this TabPanelItem.
25212      * @type Roo.Element
25213      */
25214     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25215     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25216     this.bodyEl.setStyle("display", "block");
25217     this.bodyEl.setStyle("zoom", "1");
25218     this.hideAction();
25219
25220     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25221     /** @private */
25222     this.el = Roo.get(els.el, true);
25223     this.inner = Roo.get(els.inner, true);
25224     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25225     this.pnode = Roo.get(els.el.parentNode, true);
25226     this.el.on("mousedown", this.onTabMouseDown, this);
25227     this.el.on("click", this.onTabClick, this);
25228     /** @private */
25229     if(closable){
25230         var c = Roo.get(els.close, true);
25231         c.dom.title = this.closeText;
25232         c.addClassOnOver("close-over");
25233         c.on("click", this.closeClick, this);
25234      }
25235
25236     this.addEvents({
25237          /**
25238          * @event activate
25239          * Fires when this tab becomes the active tab.
25240          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25241          * @param {Roo.TabPanelItem} this
25242          */
25243         "activate": true,
25244         /**
25245          * @event beforeclose
25246          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25247          * @param {Roo.TabPanelItem} this
25248          * @param {Object} e Set cancel to true on this object to cancel the close.
25249          */
25250         "beforeclose": true,
25251         /**
25252          * @event close
25253          * Fires when this tab is closed.
25254          * @param {Roo.TabPanelItem} this
25255          */
25256          "close": true,
25257         /**
25258          * @event deactivate
25259          * Fires when this tab is no longer the active tab.
25260          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25261          * @param {Roo.TabPanelItem} this
25262          */
25263          "deactivate" : true
25264     });
25265     this.hidden = false;
25266
25267     Roo.TabPanelItem.superclass.constructor.call(this);
25268 };
25269
25270 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25271     purgeListeners : function(){
25272        Roo.util.Observable.prototype.purgeListeners.call(this);
25273        this.el.removeAllListeners();
25274     },
25275     /**
25276      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25277      */
25278     show : function(){
25279         this.pnode.addClass("on");
25280         this.showAction();
25281         if(Roo.isOpera){
25282             this.tabPanel.stripWrap.repaint();
25283         }
25284         this.fireEvent("activate", this.tabPanel, this);
25285     },
25286
25287     /**
25288      * Returns true if this tab is the active tab.
25289      * @return {Boolean}
25290      */
25291     isActive : function(){
25292         return this.tabPanel.getActiveTab() == this;
25293     },
25294
25295     /**
25296      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25297      */
25298     hide : function(){
25299         this.pnode.removeClass("on");
25300         this.hideAction();
25301         this.fireEvent("deactivate", this.tabPanel, this);
25302     },
25303
25304     hideAction : function(){
25305         this.bodyEl.hide();
25306         this.bodyEl.setStyle("position", "absolute");
25307         this.bodyEl.setLeft("-20000px");
25308         this.bodyEl.setTop("-20000px");
25309     },
25310
25311     showAction : function(){
25312         this.bodyEl.setStyle("position", "relative");
25313         this.bodyEl.setTop("");
25314         this.bodyEl.setLeft("");
25315         this.bodyEl.show();
25316     },
25317
25318     /**
25319      * Set the tooltip for the tab.
25320      * @param {String} tooltip The tab's tooltip
25321      */
25322     setTooltip : function(text){
25323         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25324             this.textEl.dom.qtip = text;
25325             this.textEl.dom.removeAttribute('title');
25326         }else{
25327             this.textEl.dom.title = text;
25328         }
25329     },
25330
25331     onTabClick : function(e){
25332         e.preventDefault();
25333         this.tabPanel.activate(this.id);
25334     },
25335
25336     onTabMouseDown : function(e){
25337         e.preventDefault();
25338         this.tabPanel.activate(this.id);
25339     },
25340
25341     getWidth : function(){
25342         return this.inner.getWidth();
25343     },
25344
25345     setWidth : function(width){
25346         var iwidth = width - this.pnode.getPadding("lr");
25347         this.inner.setWidth(iwidth);
25348         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25349         this.pnode.setWidth(width);
25350     },
25351
25352     /**
25353      * Show or hide the tab
25354      * @param {Boolean} hidden True to hide or false to show.
25355      */
25356     setHidden : function(hidden){
25357         this.hidden = hidden;
25358         this.pnode.setStyle("display", hidden ? "none" : "");
25359     },
25360
25361     /**
25362      * Returns true if this tab is "hidden"
25363      * @return {Boolean}
25364      */
25365     isHidden : function(){
25366         return this.hidden;
25367     },
25368
25369     /**
25370      * Returns the text for this tab
25371      * @return {String}
25372      */
25373     getText : function(){
25374         return this.text;
25375     },
25376
25377     autoSize : function(){
25378         //this.el.beginMeasure();
25379         this.textEl.setWidth(1);
25380         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25381         //this.el.endMeasure();
25382     },
25383
25384     /**
25385      * Sets the text for the tab (Note: this also sets the tooltip text)
25386      * @param {String} text The tab's text and tooltip
25387      */
25388     setText : function(text){
25389         this.text = text;
25390         this.textEl.update(text);
25391         this.setTooltip(text);
25392         if(!this.tabPanel.resizeTabs){
25393             this.autoSize();
25394         }
25395     },
25396     /**
25397      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25398      */
25399     activate : function(){
25400         this.tabPanel.activate(this.id);
25401     },
25402
25403     /**
25404      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25405      */
25406     disable : function(){
25407         if(this.tabPanel.active != this){
25408             this.disabled = true;
25409             this.pnode.addClass("disabled");
25410         }
25411     },
25412
25413     /**
25414      * Enables this TabPanelItem if it was previously disabled.
25415      */
25416     enable : function(){
25417         this.disabled = false;
25418         this.pnode.removeClass("disabled");
25419     },
25420
25421     /**
25422      * Sets the content for this TabPanelItem.
25423      * @param {String} content The content
25424      * @param {Boolean} loadScripts true to look for and load scripts
25425      */
25426     setContent : function(content, loadScripts){
25427         this.bodyEl.update(content, loadScripts);
25428     },
25429
25430     /**
25431      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25432      * @return {Roo.UpdateManager} The UpdateManager
25433      */
25434     getUpdateManager : function(){
25435         return this.bodyEl.getUpdateManager();
25436     },
25437
25438     /**
25439      * Set a URL to be used to load the content for this TabPanelItem.
25440      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25441      * @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)
25442      * @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)
25443      * @return {Roo.UpdateManager} The UpdateManager
25444      */
25445     setUrl : function(url, params, loadOnce){
25446         if(this.refreshDelegate){
25447             this.un('activate', this.refreshDelegate);
25448         }
25449         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25450         this.on("activate", this.refreshDelegate);
25451         return this.bodyEl.getUpdateManager();
25452     },
25453
25454     /** @private */
25455     _handleRefresh : function(url, params, loadOnce){
25456         if(!loadOnce || !this.loaded){
25457             var updater = this.bodyEl.getUpdateManager();
25458             updater.update(url, params, this._setLoaded.createDelegate(this));
25459         }
25460     },
25461
25462     /**
25463      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25464      *   Will fail silently if the setUrl method has not been called.
25465      *   This does not activate the panel, just updates its content.
25466      */
25467     refresh : function(){
25468         if(this.refreshDelegate){
25469            this.loaded = false;
25470            this.refreshDelegate();
25471         }
25472     },
25473
25474     /** @private */
25475     _setLoaded : function(){
25476         this.loaded = true;
25477     },
25478
25479     /** @private */
25480     closeClick : function(e){
25481         var o = {};
25482         e.stopEvent();
25483         this.fireEvent("beforeclose", this, o);
25484         if(o.cancel !== true){
25485             this.tabPanel.removeTab(this.id);
25486         }
25487     },
25488     /**
25489      * The text displayed in the tooltip for the close icon.
25490      * @type String
25491      */
25492     closeText : "Close this tab"
25493 });
25494
25495 /** @private */
25496 Roo.TabPanel.prototype.createStrip = function(container){
25497     var strip = document.createElement("div");
25498     strip.className = "x-tabs-wrap";
25499     container.appendChild(strip);
25500     return strip;
25501 };
25502 /** @private */
25503 Roo.TabPanel.prototype.createStripList = function(strip){
25504     // div wrapper for retard IE
25505     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>';
25506     return strip.firstChild.firstChild.firstChild.firstChild;
25507 };
25508 /** @private */
25509 Roo.TabPanel.prototype.createBody = function(container){
25510     var body = document.createElement("div");
25511     Roo.id(body, "tab-body");
25512     Roo.fly(body).addClass("x-tabs-body");
25513     container.appendChild(body);
25514     return body;
25515 };
25516 /** @private */
25517 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25518     var body = Roo.getDom(id);
25519     if(!body){
25520         body = document.createElement("div");
25521         body.id = id;
25522     }
25523     Roo.fly(body).addClass("x-tabs-item-body");
25524     bodyEl.insertBefore(body, bodyEl.firstChild);
25525     return body;
25526 };
25527 /** @private */
25528 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25529     var td = document.createElement("td");
25530     stripEl.appendChild(td);
25531     if(closable){
25532         td.className = "x-tabs-closable";
25533         if(!this.closeTpl){
25534             this.closeTpl = new Roo.Template(
25535                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25536                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25537                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25538             );
25539         }
25540         var el = this.closeTpl.overwrite(td, {"text": text});
25541         var close = el.getElementsByTagName("div")[0];
25542         var inner = el.getElementsByTagName("em")[0];
25543         return {"el": el, "close": close, "inner": inner};
25544     } else {
25545         if(!this.tabTpl){
25546             this.tabTpl = new Roo.Template(
25547                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25548                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25549             );
25550         }
25551         var el = this.tabTpl.overwrite(td, {"text": text});
25552         var inner = el.getElementsByTagName("em")[0];
25553         return {"el": el, "inner": inner};
25554     }
25555 };/*
25556  * Based on:
25557  * Ext JS Library 1.1.1
25558  * Copyright(c) 2006-2007, Ext JS, LLC.
25559  *
25560  * Originally Released Under LGPL - original licence link has changed is not relivant.
25561  *
25562  * Fork - LGPL
25563  * <script type="text/javascript">
25564  */
25565
25566 /**
25567  * @class Roo.Button
25568  * @extends Roo.util.Observable
25569  * Simple Button class
25570  * @cfg {String} text The button text
25571  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25572  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25573  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25574  * @cfg {Object} scope The scope of the handler
25575  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25576  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25577  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25578  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25579  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25580  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25581    applies if enableToggle = true)
25582  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25583  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25584   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25585  * @constructor
25586  * Create a new button
25587  * @param {Object} config The config object
25588  */
25589 Roo.Button = function(renderTo, config)
25590 {
25591     if (!config) {
25592         config = renderTo;
25593         renderTo = config.renderTo || false;
25594     }
25595     
25596     Roo.apply(this, config);
25597     this.addEvents({
25598         /**
25599              * @event click
25600              * Fires when this button is clicked
25601              * @param {Button} this
25602              * @param {EventObject} e The click event
25603              */
25604             "click" : true,
25605         /**
25606              * @event toggle
25607              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25608              * @param {Button} this
25609              * @param {Boolean} pressed
25610              */
25611             "toggle" : true,
25612         /**
25613              * @event mouseover
25614              * Fires when the mouse hovers over the button
25615              * @param {Button} this
25616              * @param {Event} e The event object
25617              */
25618         'mouseover' : true,
25619         /**
25620              * @event mouseout
25621              * Fires when the mouse exits the button
25622              * @param {Button} this
25623              * @param {Event} e The event object
25624              */
25625         'mouseout': true,
25626          /**
25627              * @event render
25628              * Fires when the button is rendered
25629              * @param {Button} this
25630              */
25631         'render': true
25632     });
25633     if(this.menu){
25634         this.menu = Roo.menu.MenuMgr.get(this.menu);
25635     }
25636     if(renderTo){
25637         this.render(renderTo);
25638     }
25639     
25640     Roo.util.Observable.call(this);
25641 };
25642
25643 Roo.extend(Roo.Button, Roo.util.Observable, {
25644     /**
25645      * 
25646      */
25647     
25648     /**
25649      * Read-only. True if this button is hidden
25650      * @type Boolean
25651      */
25652     hidden : false,
25653     /**
25654      * Read-only. True if this button is disabled
25655      * @type Boolean
25656      */
25657     disabled : false,
25658     /**
25659      * Read-only. True if this button is pressed (only if enableToggle = true)
25660      * @type Boolean
25661      */
25662     pressed : false,
25663
25664     /**
25665      * @cfg {Number} tabIndex 
25666      * The DOM tabIndex for this button (defaults to undefined)
25667      */
25668     tabIndex : undefined,
25669
25670     /**
25671      * @cfg {Boolean} enableToggle
25672      * True to enable pressed/not pressed toggling (defaults to false)
25673      */
25674     enableToggle: false,
25675     /**
25676      * @cfg {Mixed} menu
25677      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25678      */
25679     menu : undefined,
25680     /**
25681      * @cfg {String} menuAlign
25682      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25683      */
25684     menuAlign : "tl-bl?",
25685
25686     /**
25687      * @cfg {String} iconCls
25688      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25689      */
25690     iconCls : undefined,
25691     /**
25692      * @cfg {String} type
25693      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25694      */
25695     type : 'button',
25696
25697     // private
25698     menuClassTarget: 'tr',
25699
25700     /**
25701      * @cfg {String} clickEvent
25702      * The type of event to map to the button's event handler (defaults to 'click')
25703      */
25704     clickEvent : 'click',
25705
25706     /**
25707      * @cfg {Boolean} handleMouseEvents
25708      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25709      */
25710     handleMouseEvents : true,
25711
25712     /**
25713      * @cfg {String} tooltipType
25714      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25715      */
25716     tooltipType : 'qtip',
25717
25718     /**
25719      * @cfg {String} cls
25720      * A CSS class to apply to the button's main element.
25721      */
25722     
25723     /**
25724      * @cfg {Roo.Template} template (Optional)
25725      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25726      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25727      * require code modifications if required elements (e.g. a button) aren't present.
25728      */
25729
25730     // private
25731     render : function(renderTo){
25732         var btn;
25733         if(this.hideParent){
25734             this.parentEl = Roo.get(renderTo);
25735         }
25736         if(!this.dhconfig){
25737             if(!this.template){
25738                 if(!Roo.Button.buttonTemplate){
25739                     // hideous table template
25740                     Roo.Button.buttonTemplate = new Roo.Template(
25741                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25742                         '<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>',
25743                         "</tr></tbody></table>");
25744                 }
25745                 this.template = Roo.Button.buttonTemplate;
25746             }
25747             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25748             var btnEl = btn.child("button:first");
25749             btnEl.on('focus', this.onFocus, this);
25750             btnEl.on('blur', this.onBlur, this);
25751             if(this.cls){
25752                 btn.addClass(this.cls);
25753             }
25754             if(this.icon){
25755                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25756             }
25757             if(this.iconCls){
25758                 btnEl.addClass(this.iconCls);
25759                 if(!this.cls){
25760                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25761                 }
25762             }
25763             if(this.tabIndex !== undefined){
25764                 btnEl.dom.tabIndex = this.tabIndex;
25765             }
25766             if(this.tooltip){
25767                 if(typeof this.tooltip == 'object'){
25768                     Roo.QuickTips.tips(Roo.apply({
25769                           target: btnEl.id
25770                     }, this.tooltip));
25771                 } else {
25772                     btnEl.dom[this.tooltipType] = this.tooltip;
25773                 }
25774             }
25775         }else{
25776             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25777         }
25778         this.el = btn;
25779         if(this.id){
25780             this.el.dom.id = this.el.id = this.id;
25781         }
25782         if(this.menu){
25783             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25784             this.menu.on("show", this.onMenuShow, this);
25785             this.menu.on("hide", this.onMenuHide, this);
25786         }
25787         btn.addClass("x-btn");
25788         if(Roo.isIE && !Roo.isIE7){
25789             this.autoWidth.defer(1, this);
25790         }else{
25791             this.autoWidth();
25792         }
25793         if(this.handleMouseEvents){
25794             btn.on("mouseover", this.onMouseOver, this);
25795             btn.on("mouseout", this.onMouseOut, this);
25796             btn.on("mousedown", this.onMouseDown, this);
25797         }
25798         btn.on(this.clickEvent, this.onClick, this);
25799         //btn.on("mouseup", this.onMouseUp, this);
25800         if(this.hidden){
25801             this.hide();
25802         }
25803         if(this.disabled){
25804             this.disable();
25805         }
25806         Roo.ButtonToggleMgr.register(this);
25807         if(this.pressed){
25808             this.el.addClass("x-btn-pressed");
25809         }
25810         if(this.repeat){
25811             var repeater = new Roo.util.ClickRepeater(btn,
25812                 typeof this.repeat == "object" ? this.repeat : {}
25813             );
25814             repeater.on("click", this.onClick,  this);
25815         }
25816         this.fireEvent('render', this);
25817         
25818     },
25819     /**
25820      * Returns the button's underlying element
25821      * @return {Roo.Element} The element
25822      */
25823     getEl : function(){
25824         return this.el;  
25825     },
25826     
25827     /**
25828      * Destroys this Button and removes any listeners.
25829      */
25830     destroy : function(){
25831         Roo.ButtonToggleMgr.unregister(this);
25832         this.el.removeAllListeners();
25833         this.purgeListeners();
25834         this.el.remove();
25835     },
25836
25837     // private
25838     autoWidth : function(){
25839         if(this.el){
25840             this.el.setWidth("auto");
25841             if(Roo.isIE7 && Roo.isStrict){
25842                 var ib = this.el.child('button');
25843                 if(ib && ib.getWidth() > 20){
25844                     ib.clip();
25845                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25846                 }
25847             }
25848             if(this.minWidth){
25849                 if(this.hidden){
25850                     this.el.beginMeasure();
25851                 }
25852                 if(this.el.getWidth() < this.minWidth){
25853                     this.el.setWidth(this.minWidth);
25854                 }
25855                 if(this.hidden){
25856                     this.el.endMeasure();
25857                 }
25858             }
25859         }
25860     },
25861
25862     /**
25863      * Assigns this button's click handler
25864      * @param {Function} handler The function to call when the button is clicked
25865      * @param {Object} scope (optional) Scope for the function passed in
25866      */
25867     setHandler : function(handler, scope){
25868         this.handler = handler;
25869         this.scope = scope;  
25870     },
25871     
25872     /**
25873      * Sets this button's text
25874      * @param {String} text The button text
25875      */
25876     setText : function(text){
25877         this.text = text;
25878         if(this.el){
25879             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25880         }
25881         this.autoWidth();
25882     },
25883     
25884     /**
25885      * Gets the text for this button
25886      * @return {String} The button text
25887      */
25888     getText : function(){
25889         return this.text;  
25890     },
25891     
25892     /**
25893      * Show this button
25894      */
25895     show: function(){
25896         this.hidden = false;
25897         if(this.el){
25898             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25899         }
25900     },
25901     
25902     /**
25903      * Hide this button
25904      */
25905     hide: function(){
25906         this.hidden = true;
25907         if(this.el){
25908             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25909         }
25910     },
25911     
25912     /**
25913      * Convenience function for boolean show/hide
25914      * @param {Boolean} visible True to show, false to hide
25915      */
25916     setVisible: function(visible){
25917         if(visible) {
25918             this.show();
25919         }else{
25920             this.hide();
25921         }
25922     },
25923     
25924     /**
25925      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25926      * @param {Boolean} state (optional) Force a particular state
25927      */
25928     toggle : function(state){
25929         state = state === undefined ? !this.pressed : state;
25930         if(state != this.pressed){
25931             if(state){
25932                 this.el.addClass("x-btn-pressed");
25933                 this.pressed = true;
25934                 this.fireEvent("toggle", this, true);
25935             }else{
25936                 this.el.removeClass("x-btn-pressed");
25937                 this.pressed = false;
25938                 this.fireEvent("toggle", this, false);
25939             }
25940             if(this.toggleHandler){
25941                 this.toggleHandler.call(this.scope || this, this, state);
25942             }
25943         }
25944     },
25945     
25946     /**
25947      * Focus the button
25948      */
25949     focus : function(){
25950         this.el.child('button:first').focus();
25951     },
25952     
25953     /**
25954      * Disable this button
25955      */
25956     disable : function(){
25957         if(this.el){
25958             this.el.addClass("x-btn-disabled");
25959         }
25960         this.disabled = true;
25961     },
25962     
25963     /**
25964      * Enable this button
25965      */
25966     enable : function(){
25967         if(this.el){
25968             this.el.removeClass("x-btn-disabled");
25969         }
25970         this.disabled = false;
25971     },
25972
25973     /**
25974      * Convenience function for boolean enable/disable
25975      * @param {Boolean} enabled True to enable, false to disable
25976      */
25977     setDisabled : function(v){
25978         this[v !== true ? "enable" : "disable"]();
25979     },
25980
25981     // private
25982     onClick : function(e){
25983         if(e){
25984             e.preventDefault();
25985         }
25986         if(e.button != 0){
25987             return;
25988         }
25989         if(!this.disabled){
25990             if(this.enableToggle){
25991                 this.toggle();
25992             }
25993             if(this.menu && !this.menu.isVisible()){
25994                 this.menu.show(this.el, this.menuAlign);
25995             }
25996             this.fireEvent("click", this, e);
25997             if(this.handler){
25998                 this.el.removeClass("x-btn-over");
25999                 this.handler.call(this.scope || this, this, e);
26000             }
26001         }
26002     },
26003     // private
26004     onMouseOver : function(e){
26005         if(!this.disabled){
26006             this.el.addClass("x-btn-over");
26007             this.fireEvent('mouseover', this, e);
26008         }
26009     },
26010     // private
26011     onMouseOut : function(e){
26012         if(!e.within(this.el,  true)){
26013             this.el.removeClass("x-btn-over");
26014             this.fireEvent('mouseout', this, e);
26015         }
26016     },
26017     // private
26018     onFocus : function(e){
26019         if(!this.disabled){
26020             this.el.addClass("x-btn-focus");
26021         }
26022     },
26023     // private
26024     onBlur : function(e){
26025         this.el.removeClass("x-btn-focus");
26026     },
26027     // private
26028     onMouseDown : function(e){
26029         if(!this.disabled && e.button == 0){
26030             this.el.addClass("x-btn-click");
26031             Roo.get(document).on('mouseup', this.onMouseUp, this);
26032         }
26033     },
26034     // private
26035     onMouseUp : function(e){
26036         if(e.button == 0){
26037             this.el.removeClass("x-btn-click");
26038             Roo.get(document).un('mouseup', this.onMouseUp, this);
26039         }
26040     },
26041     // private
26042     onMenuShow : function(e){
26043         this.el.addClass("x-btn-menu-active");
26044     },
26045     // private
26046     onMenuHide : function(e){
26047         this.el.removeClass("x-btn-menu-active");
26048     }   
26049 });
26050
26051 // Private utility class used by Button
26052 Roo.ButtonToggleMgr = function(){
26053    var groups = {};
26054    
26055    function toggleGroup(btn, state){
26056        if(state){
26057            var g = groups[btn.toggleGroup];
26058            for(var i = 0, l = g.length; i < l; i++){
26059                if(g[i] != btn){
26060                    g[i].toggle(false);
26061                }
26062            }
26063        }
26064    }
26065    
26066    return {
26067        register : function(btn){
26068            if(!btn.toggleGroup){
26069                return;
26070            }
26071            var g = groups[btn.toggleGroup];
26072            if(!g){
26073                g = groups[btn.toggleGroup] = [];
26074            }
26075            g.push(btn);
26076            btn.on("toggle", toggleGroup);
26077        },
26078        
26079        unregister : function(btn){
26080            if(!btn.toggleGroup){
26081                return;
26082            }
26083            var g = groups[btn.toggleGroup];
26084            if(g){
26085                g.remove(btn);
26086                btn.un("toggle", toggleGroup);
26087            }
26088        }
26089    };
26090 }();/*
26091  * Based on:
26092  * Ext JS Library 1.1.1
26093  * Copyright(c) 2006-2007, Ext JS, LLC.
26094  *
26095  * Originally Released Under LGPL - original licence link has changed is not relivant.
26096  *
26097  * Fork - LGPL
26098  * <script type="text/javascript">
26099  */
26100  
26101 /**
26102  * @class Roo.SplitButton
26103  * @extends Roo.Button
26104  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26105  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26106  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26107  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26108  * @cfg {String} arrowTooltip The title attribute of the arrow
26109  * @constructor
26110  * Create a new menu button
26111  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26112  * @param {Object} config The config object
26113  */
26114 Roo.SplitButton = function(renderTo, config){
26115     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26116     /**
26117      * @event arrowclick
26118      * Fires when this button's arrow is clicked
26119      * @param {SplitButton} this
26120      * @param {EventObject} e The click event
26121      */
26122     this.addEvents({"arrowclick":true});
26123 };
26124
26125 Roo.extend(Roo.SplitButton, Roo.Button, {
26126     render : function(renderTo){
26127         // this is one sweet looking template!
26128         var tpl = new Roo.Template(
26129             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26130             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26131             '<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>',
26132             "</tbody></table></td><td>",
26133             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26134             '<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>',
26135             "</tbody></table></td></tr></table>"
26136         );
26137         var btn = tpl.append(renderTo, [this.text, this.type], true);
26138         var btnEl = btn.child("button");
26139         if(this.cls){
26140             btn.addClass(this.cls);
26141         }
26142         if(this.icon){
26143             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26144         }
26145         if(this.iconCls){
26146             btnEl.addClass(this.iconCls);
26147             if(!this.cls){
26148                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26149             }
26150         }
26151         this.el = btn;
26152         if(this.handleMouseEvents){
26153             btn.on("mouseover", this.onMouseOver, this);
26154             btn.on("mouseout", this.onMouseOut, this);
26155             btn.on("mousedown", this.onMouseDown, this);
26156             btn.on("mouseup", this.onMouseUp, this);
26157         }
26158         btn.on(this.clickEvent, this.onClick, this);
26159         if(this.tooltip){
26160             if(typeof this.tooltip == 'object'){
26161                 Roo.QuickTips.tips(Roo.apply({
26162                       target: btnEl.id
26163                 }, this.tooltip));
26164             } else {
26165                 btnEl.dom[this.tooltipType] = this.tooltip;
26166             }
26167         }
26168         if(this.arrowTooltip){
26169             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26170         }
26171         if(this.hidden){
26172             this.hide();
26173         }
26174         if(this.disabled){
26175             this.disable();
26176         }
26177         if(this.pressed){
26178             this.el.addClass("x-btn-pressed");
26179         }
26180         if(Roo.isIE && !Roo.isIE7){
26181             this.autoWidth.defer(1, this);
26182         }else{
26183             this.autoWidth();
26184         }
26185         if(this.menu){
26186             this.menu.on("show", this.onMenuShow, this);
26187             this.menu.on("hide", this.onMenuHide, this);
26188         }
26189         this.fireEvent('render', this);
26190     },
26191
26192     // private
26193     autoWidth : function(){
26194         if(this.el){
26195             var tbl = this.el.child("table:first");
26196             var tbl2 = this.el.child("table:last");
26197             this.el.setWidth("auto");
26198             tbl.setWidth("auto");
26199             if(Roo.isIE7 && Roo.isStrict){
26200                 var ib = this.el.child('button:first');
26201                 if(ib && ib.getWidth() > 20){
26202                     ib.clip();
26203                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26204                 }
26205             }
26206             if(this.minWidth){
26207                 if(this.hidden){
26208                     this.el.beginMeasure();
26209                 }
26210                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26211                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26212                 }
26213                 if(this.hidden){
26214                     this.el.endMeasure();
26215                 }
26216             }
26217             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26218         } 
26219     },
26220     /**
26221      * Sets this button's click handler
26222      * @param {Function} handler The function to call when the button is clicked
26223      * @param {Object} scope (optional) Scope for the function passed above
26224      */
26225     setHandler : function(handler, scope){
26226         this.handler = handler;
26227         this.scope = scope;  
26228     },
26229     
26230     /**
26231      * Sets this button's arrow click handler
26232      * @param {Function} handler The function to call when the arrow is clicked
26233      * @param {Object} scope (optional) Scope for the function passed above
26234      */
26235     setArrowHandler : function(handler, scope){
26236         this.arrowHandler = handler;
26237         this.scope = scope;  
26238     },
26239     
26240     /**
26241      * Focus the button
26242      */
26243     focus : function(){
26244         if(this.el){
26245             this.el.child("button:first").focus();
26246         }
26247     },
26248
26249     // private
26250     onClick : function(e){
26251         e.preventDefault();
26252         if(!this.disabled){
26253             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26254                 if(this.menu && !this.menu.isVisible()){
26255                     this.menu.show(this.el, this.menuAlign);
26256                 }
26257                 this.fireEvent("arrowclick", this, e);
26258                 if(this.arrowHandler){
26259                     this.arrowHandler.call(this.scope || this, this, e);
26260                 }
26261             }else{
26262                 this.fireEvent("click", this, e);
26263                 if(this.handler){
26264                     this.handler.call(this.scope || this, this, e);
26265                 }
26266             }
26267         }
26268     },
26269     // private
26270     onMouseDown : function(e){
26271         if(!this.disabled){
26272             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26273         }
26274     },
26275     // private
26276     onMouseUp : function(e){
26277         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26278     }   
26279 });
26280
26281
26282 // backwards compat
26283 Roo.MenuButton = Roo.SplitButton;/*
26284  * Based on:
26285  * Ext JS Library 1.1.1
26286  * Copyright(c) 2006-2007, Ext JS, LLC.
26287  *
26288  * Originally Released Under LGPL - original licence link has changed is not relivant.
26289  *
26290  * Fork - LGPL
26291  * <script type="text/javascript">
26292  */
26293
26294 /**
26295  * @class Roo.Toolbar
26296  * Basic Toolbar class.
26297  * @constructor
26298  * Creates a new Toolbar
26299  * @param {Object} config The config object
26300  */ 
26301 Roo.Toolbar = function(container, buttons, config)
26302 {
26303     /// old consturctor format still supported..
26304     if(container instanceof Array){ // omit the container for later rendering
26305         buttons = container;
26306         config = buttons;
26307         container = null;
26308     }
26309     if (typeof(container) == 'object' && container.xtype) {
26310         config = container;
26311         container = config.container;
26312         buttons = config.buttons; // not really - use items!!
26313     }
26314     var xitems = [];
26315     if (config && config.items) {
26316         xitems = config.items;
26317         delete config.items;
26318     }
26319     Roo.apply(this, config);
26320     this.buttons = buttons;
26321     
26322     if(container){
26323         this.render(container);
26324     }
26325     Roo.each(xitems, function(b) {
26326         this.add(b);
26327     }, this);
26328     
26329 };
26330
26331 Roo.Toolbar.prototype = {
26332     /**
26333      * @cfg {Roo.data.Store} items
26334      * array of button configs or elements to add
26335      */
26336     
26337     /**
26338      * @cfg {String/HTMLElement/Element} container
26339      * The id or element that will contain the toolbar
26340      */
26341     // private
26342     render : function(ct){
26343         this.el = Roo.get(ct);
26344         if(this.cls){
26345             this.el.addClass(this.cls);
26346         }
26347         // using a table allows for vertical alignment
26348         // 100% width is needed by Safari...
26349         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26350         this.tr = this.el.child("tr", true);
26351         var autoId = 0;
26352         this.items = new Roo.util.MixedCollection(false, function(o){
26353             return o.id || ("item" + (++autoId));
26354         });
26355         if(this.buttons){
26356             this.add.apply(this, this.buttons);
26357             delete this.buttons;
26358         }
26359     },
26360
26361     /**
26362      * Adds element(s) to the toolbar -- this function takes a variable number of 
26363      * arguments of mixed type and adds them to the toolbar.
26364      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26365      * <ul>
26366      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26367      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26368      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26369      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26370      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26371      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26372      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26373      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26374      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26375      * </ul>
26376      * @param {Mixed} arg2
26377      * @param {Mixed} etc.
26378      */
26379     add : function(){
26380         var a = arguments, l = a.length;
26381         for(var i = 0; i < l; i++){
26382             this._add(a[i]);
26383         }
26384     },
26385     // private..
26386     _add : function(el) {
26387         
26388         if (el.xtype) {
26389             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26390         }
26391         
26392         if (el.applyTo){ // some kind of form field
26393             return this.addField(el);
26394         } 
26395         if (el.render){ // some kind of Toolbar.Item
26396             return this.addItem(el);
26397         }
26398         if (typeof el == "string"){ // string
26399             if(el == "separator" || el == "-"){
26400                 return this.addSeparator();
26401             }
26402             if (el == " "){
26403                 return this.addSpacer();
26404             }
26405             if(el == "->"){
26406                 return this.addFill();
26407             }
26408             return this.addText(el);
26409             
26410         }
26411         if(el.tagName){ // element
26412             return this.addElement(el);
26413         }
26414         if(typeof el == "object"){ // must be button config?
26415             return this.addButton(el);
26416         }
26417         // and now what?!?!
26418         return false;
26419         
26420     },
26421     
26422     /**
26423      * Add an Xtype element
26424      * @param {Object} xtype Xtype Object
26425      * @return {Object} created Object
26426      */
26427     addxtype : function(e){
26428         return this.add(e);  
26429     },
26430     
26431     /**
26432      * Returns the Element for this toolbar.
26433      * @return {Roo.Element}
26434      */
26435     getEl : function(){
26436         return this.el;  
26437     },
26438     
26439     /**
26440      * Adds a separator
26441      * @return {Roo.Toolbar.Item} The separator item
26442      */
26443     addSeparator : function(){
26444         return this.addItem(new Roo.Toolbar.Separator());
26445     },
26446
26447     /**
26448      * Adds a spacer element
26449      * @return {Roo.Toolbar.Spacer} The spacer item
26450      */
26451     addSpacer : function(){
26452         return this.addItem(new Roo.Toolbar.Spacer());
26453     },
26454
26455     /**
26456      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26457      * @return {Roo.Toolbar.Fill} The fill item
26458      */
26459     addFill : function(){
26460         return this.addItem(new Roo.Toolbar.Fill());
26461     },
26462
26463     /**
26464      * Adds any standard HTML element to the toolbar
26465      * @param {String/HTMLElement/Element} el The element or id of the element to add
26466      * @return {Roo.Toolbar.Item} The element's item
26467      */
26468     addElement : function(el){
26469         return this.addItem(new Roo.Toolbar.Item(el));
26470     },
26471     /**
26472      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26473      * @type Roo.util.MixedCollection  
26474      */
26475     items : false,
26476      
26477     /**
26478      * Adds any Toolbar.Item or subclass
26479      * @param {Roo.Toolbar.Item} item
26480      * @return {Roo.Toolbar.Item} The item
26481      */
26482     addItem : function(item){
26483         var td = this.nextBlock();
26484         item.render(td);
26485         this.items.add(item);
26486         return item;
26487     },
26488     
26489     /**
26490      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26491      * @param {Object/Array} config A button config or array of configs
26492      * @return {Roo.Toolbar.Button/Array}
26493      */
26494     addButton : function(config){
26495         if(config instanceof Array){
26496             var buttons = [];
26497             for(var i = 0, len = config.length; i < len; i++) {
26498                 buttons.push(this.addButton(config[i]));
26499             }
26500             return buttons;
26501         }
26502         var b = config;
26503         if(!(config instanceof Roo.Toolbar.Button)){
26504             b = config.split ?
26505                 new Roo.Toolbar.SplitButton(config) :
26506                 new Roo.Toolbar.Button(config);
26507         }
26508         var td = this.nextBlock();
26509         b.render(td);
26510         this.items.add(b);
26511         return b;
26512     },
26513     
26514     /**
26515      * Adds text to the toolbar
26516      * @param {String} text The text to add
26517      * @return {Roo.Toolbar.Item} The element's item
26518      */
26519     addText : function(text){
26520         return this.addItem(new Roo.Toolbar.TextItem(text));
26521     },
26522     
26523     /**
26524      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26525      * @param {Number} index The index where the item is to be inserted
26526      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26527      * @return {Roo.Toolbar.Button/Item}
26528      */
26529     insertButton : function(index, item){
26530         if(item instanceof Array){
26531             var buttons = [];
26532             for(var i = 0, len = item.length; i < len; i++) {
26533                buttons.push(this.insertButton(index + i, item[i]));
26534             }
26535             return buttons;
26536         }
26537         if (!(item instanceof Roo.Toolbar.Button)){
26538            item = new Roo.Toolbar.Button(item);
26539         }
26540         var td = document.createElement("td");
26541         this.tr.insertBefore(td, this.tr.childNodes[index]);
26542         item.render(td);
26543         this.items.insert(index, item);
26544         return item;
26545     },
26546     
26547     /**
26548      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26549      * @param {Object} config
26550      * @return {Roo.Toolbar.Item} The element's item
26551      */
26552     addDom : function(config, returnEl){
26553         var td = this.nextBlock();
26554         Roo.DomHelper.overwrite(td, config);
26555         var ti = new Roo.Toolbar.Item(td.firstChild);
26556         ti.render(td);
26557         this.items.add(ti);
26558         return ti;
26559     },
26560
26561     /**
26562      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26563      * @type Roo.util.MixedCollection  
26564      */
26565     fields : false,
26566     
26567     /**
26568      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26569      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26570      * @param {Roo.form.Field} field
26571      * @return {Roo.ToolbarItem}
26572      */
26573      
26574       
26575     addField : function(field) {
26576         if (!this.fields) {
26577             var autoId = 0;
26578             this.fields = new Roo.util.MixedCollection(false, function(o){
26579                 return o.id || ("item" + (++autoId));
26580             });
26581
26582         }
26583         
26584         var td = this.nextBlock();
26585         field.render(td);
26586         var ti = new Roo.Toolbar.Item(td.firstChild);
26587         ti.render(td);
26588         this.items.add(ti);
26589         this.fields.add(field);
26590         return ti;
26591     },
26592     /**
26593      * Hide the toolbar
26594      * @method hide
26595      */
26596      
26597       
26598     hide : function()
26599     {
26600         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26601         this.el.child('div').hide();
26602     },
26603     /**
26604      * Show the toolbar
26605      * @method show
26606      */
26607     show : function()
26608     {
26609         this.el.child('div').show();
26610     },
26611       
26612     // private
26613     nextBlock : function(){
26614         var td = document.createElement("td");
26615         this.tr.appendChild(td);
26616         return td;
26617     },
26618
26619     // private
26620     destroy : function(){
26621         if(this.items){ // rendered?
26622             Roo.destroy.apply(Roo, this.items.items);
26623         }
26624         if(this.fields){ // rendered?
26625             Roo.destroy.apply(Roo, this.fields.items);
26626         }
26627         Roo.Element.uncache(this.el, this.tr);
26628     }
26629 };
26630
26631 /**
26632  * @class Roo.Toolbar.Item
26633  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26634  * @constructor
26635  * Creates a new Item
26636  * @param {HTMLElement} el 
26637  */
26638 Roo.Toolbar.Item = function(el){
26639     this.el = Roo.getDom(el);
26640     this.id = Roo.id(this.el);
26641     this.hidden = false;
26642 };
26643
26644 Roo.Toolbar.Item.prototype = {
26645     
26646     /**
26647      * Get this item's HTML Element
26648      * @return {HTMLElement}
26649      */
26650     getEl : function(){
26651        return this.el;  
26652     },
26653
26654     // private
26655     render : function(td){
26656         this.td = td;
26657         td.appendChild(this.el);
26658     },
26659     
26660     /**
26661      * Removes and destroys this item.
26662      */
26663     destroy : function(){
26664         this.td.parentNode.removeChild(this.td);
26665     },
26666     
26667     /**
26668      * Shows this item.
26669      */
26670     show: function(){
26671         this.hidden = false;
26672         this.td.style.display = "";
26673     },
26674     
26675     /**
26676      * Hides this item.
26677      */
26678     hide: function(){
26679         this.hidden = true;
26680         this.td.style.display = "none";
26681     },
26682     
26683     /**
26684      * Convenience function for boolean show/hide.
26685      * @param {Boolean} visible true to show/false to hide
26686      */
26687     setVisible: function(visible){
26688         if(visible) {
26689             this.show();
26690         }else{
26691             this.hide();
26692         }
26693     },
26694     
26695     /**
26696      * Try to focus this item.
26697      */
26698     focus : function(){
26699         Roo.fly(this.el).focus();
26700     },
26701     
26702     /**
26703      * Disables this item.
26704      */
26705     disable : function(){
26706         Roo.fly(this.td).addClass("x-item-disabled");
26707         this.disabled = true;
26708         this.el.disabled = true;
26709     },
26710     
26711     /**
26712      * Enables this item.
26713      */
26714     enable : function(){
26715         Roo.fly(this.td).removeClass("x-item-disabled");
26716         this.disabled = false;
26717         this.el.disabled = false;
26718     }
26719 };
26720
26721
26722 /**
26723  * @class Roo.Toolbar.Separator
26724  * @extends Roo.Toolbar.Item
26725  * A simple toolbar separator class
26726  * @constructor
26727  * Creates a new Separator
26728  */
26729 Roo.Toolbar.Separator = function(){
26730     var s = document.createElement("span");
26731     s.className = "ytb-sep";
26732     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26733 };
26734 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26735     enable:Roo.emptyFn,
26736     disable:Roo.emptyFn,
26737     focus:Roo.emptyFn
26738 });
26739
26740 /**
26741  * @class Roo.Toolbar.Spacer
26742  * @extends Roo.Toolbar.Item
26743  * A simple element that adds extra horizontal space to a toolbar.
26744  * @constructor
26745  * Creates a new Spacer
26746  */
26747 Roo.Toolbar.Spacer = function(){
26748     var s = document.createElement("div");
26749     s.className = "ytb-spacer";
26750     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26751 };
26752 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26753     enable:Roo.emptyFn,
26754     disable:Roo.emptyFn,
26755     focus:Roo.emptyFn
26756 });
26757
26758 /**
26759  * @class Roo.Toolbar.Fill
26760  * @extends Roo.Toolbar.Spacer
26761  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26762  * @constructor
26763  * Creates a new Spacer
26764  */
26765 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26766     // private
26767     render : function(td){
26768         td.style.width = '100%';
26769         Roo.Toolbar.Fill.superclass.render.call(this, td);
26770     }
26771 });
26772
26773 /**
26774  * @class Roo.Toolbar.TextItem
26775  * @extends Roo.Toolbar.Item
26776  * A simple class that renders text directly into a toolbar.
26777  * @constructor
26778  * Creates a new TextItem
26779  * @param {String} text
26780  */
26781 Roo.Toolbar.TextItem = function(text){
26782     if (typeof(text) == 'object') {
26783         text = text.text;
26784     }
26785     var s = document.createElement("span");
26786     s.className = "ytb-text";
26787     s.innerHTML = text;
26788     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26789 };
26790 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26791     enable:Roo.emptyFn,
26792     disable:Roo.emptyFn,
26793     focus:Roo.emptyFn
26794 });
26795
26796 /**
26797  * @class Roo.Toolbar.Button
26798  * @extends Roo.Button
26799  * A button that renders into a toolbar.
26800  * @constructor
26801  * Creates a new Button
26802  * @param {Object} config A standard {@link Roo.Button} config object
26803  */
26804 Roo.Toolbar.Button = function(config){
26805     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26806 };
26807 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26808     render : function(td){
26809         this.td = td;
26810         Roo.Toolbar.Button.superclass.render.call(this, td);
26811     },
26812     
26813     /**
26814      * Removes and destroys this button
26815      */
26816     destroy : function(){
26817         Roo.Toolbar.Button.superclass.destroy.call(this);
26818         this.td.parentNode.removeChild(this.td);
26819     },
26820     
26821     /**
26822      * Shows this button
26823      */
26824     show: function(){
26825         this.hidden = false;
26826         this.td.style.display = "";
26827     },
26828     
26829     /**
26830      * Hides this button
26831      */
26832     hide: function(){
26833         this.hidden = true;
26834         this.td.style.display = "none";
26835     },
26836
26837     /**
26838      * Disables this item
26839      */
26840     disable : function(){
26841         Roo.fly(this.td).addClass("x-item-disabled");
26842         this.disabled = true;
26843     },
26844
26845     /**
26846      * Enables this item
26847      */
26848     enable : function(){
26849         Roo.fly(this.td).removeClass("x-item-disabled");
26850         this.disabled = false;
26851     }
26852 });
26853 // backwards compat
26854 Roo.ToolbarButton = Roo.Toolbar.Button;
26855
26856 /**
26857  * @class Roo.Toolbar.SplitButton
26858  * @extends Roo.SplitButton
26859  * A menu button that renders into a toolbar.
26860  * @constructor
26861  * Creates a new SplitButton
26862  * @param {Object} config A standard {@link Roo.SplitButton} config object
26863  */
26864 Roo.Toolbar.SplitButton = function(config){
26865     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26866 };
26867 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26868     render : function(td){
26869         this.td = td;
26870         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26871     },
26872     
26873     /**
26874      * Removes and destroys this button
26875      */
26876     destroy : function(){
26877         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26878         this.td.parentNode.removeChild(this.td);
26879     },
26880     
26881     /**
26882      * Shows this button
26883      */
26884     show: function(){
26885         this.hidden = false;
26886         this.td.style.display = "";
26887     },
26888     
26889     /**
26890      * Hides this button
26891      */
26892     hide: function(){
26893         this.hidden = true;
26894         this.td.style.display = "none";
26895     }
26896 });
26897
26898 // backwards compat
26899 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26900  * Based on:
26901  * Ext JS Library 1.1.1
26902  * Copyright(c) 2006-2007, Ext JS, LLC.
26903  *
26904  * Originally Released Under LGPL - original licence link has changed is not relivant.
26905  *
26906  * Fork - LGPL
26907  * <script type="text/javascript">
26908  */
26909  
26910 /**
26911  * @class Roo.PagingToolbar
26912  * @extends Roo.Toolbar
26913  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26914  * @constructor
26915  * Create a new PagingToolbar
26916  * @param {Object} config The config object
26917  */
26918 Roo.PagingToolbar = function(el, ds, config)
26919 {
26920     // old args format still supported... - xtype is prefered..
26921     if (typeof(el) == 'object' && el.xtype) {
26922         // created from xtype...
26923         config = el;
26924         ds = el.dataSource;
26925         el = config.container;
26926     }
26927     
26928     
26929     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26930     this.ds = ds;
26931     this.cursor = 0;
26932     this.renderButtons(this.el);
26933     this.bind(ds);
26934 };
26935
26936 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26937     /**
26938      * @cfg {Roo.data.Store} dataSource
26939      * The underlying data store providing the paged data
26940      */
26941     /**
26942      * @cfg {String/HTMLElement/Element} container
26943      * container The id or element that will contain the toolbar
26944      */
26945     /**
26946      * @cfg {Boolean} displayInfo
26947      * True to display the displayMsg (defaults to false)
26948      */
26949     /**
26950      * @cfg {Number} pageSize
26951      * The number of records to display per page (defaults to 20)
26952      */
26953     pageSize: 20,
26954     /**
26955      * @cfg {String} displayMsg
26956      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26957      */
26958     displayMsg : 'Displaying {0} - {1} of {2}',
26959     /**
26960      * @cfg {String} emptyMsg
26961      * The message to display when no records are found (defaults to "No data to display")
26962      */
26963     emptyMsg : 'No data to display',
26964     /**
26965      * Customizable piece of the default paging text (defaults to "Page")
26966      * @type String
26967      */
26968     beforePageText : "Page",
26969     /**
26970      * Customizable piece of the default paging text (defaults to "of %0")
26971      * @type String
26972      */
26973     afterPageText : "of {0}",
26974     /**
26975      * Customizable piece of the default paging text (defaults to "First Page")
26976      * @type String
26977      */
26978     firstText : "First Page",
26979     /**
26980      * Customizable piece of the default paging text (defaults to "Previous Page")
26981      * @type String
26982      */
26983     prevText : "Previous Page",
26984     /**
26985      * Customizable piece of the default paging text (defaults to "Next Page")
26986      * @type String
26987      */
26988     nextText : "Next Page",
26989     /**
26990      * Customizable piece of the default paging text (defaults to "Last Page")
26991      * @type String
26992      */
26993     lastText : "Last Page",
26994     /**
26995      * Customizable piece of the default paging text (defaults to "Refresh")
26996      * @type String
26997      */
26998     refreshText : "Refresh",
26999
27000     // private
27001     renderButtons : function(el){
27002         Roo.PagingToolbar.superclass.render.call(this, el);
27003         this.first = this.addButton({
27004             tooltip: this.firstText,
27005             cls: "x-btn-icon x-grid-page-first",
27006             disabled: true,
27007             handler: this.onClick.createDelegate(this, ["first"])
27008         });
27009         this.prev = this.addButton({
27010             tooltip: this.prevText,
27011             cls: "x-btn-icon x-grid-page-prev",
27012             disabled: true,
27013             handler: this.onClick.createDelegate(this, ["prev"])
27014         });
27015         this.addSeparator();
27016         this.add(this.beforePageText);
27017         this.field = Roo.get(this.addDom({
27018            tag: "input",
27019            type: "text",
27020            size: "3",
27021            value: "1",
27022            cls: "x-grid-page-number"
27023         }).el);
27024         this.field.on("keydown", this.onPagingKeydown, this);
27025         this.field.on("focus", function(){this.dom.select();});
27026         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27027         this.field.setHeight(18);
27028         this.addSeparator();
27029         this.next = this.addButton({
27030             tooltip: this.nextText,
27031             cls: "x-btn-icon x-grid-page-next",
27032             disabled: true,
27033             handler: this.onClick.createDelegate(this, ["next"])
27034         });
27035         this.last = this.addButton({
27036             tooltip: this.lastText,
27037             cls: "x-btn-icon x-grid-page-last",
27038             disabled: true,
27039             handler: this.onClick.createDelegate(this, ["last"])
27040         });
27041         this.addSeparator();
27042         this.loading = this.addButton({
27043             tooltip: this.refreshText,
27044             cls: "x-btn-icon x-grid-loading",
27045             handler: this.onClick.createDelegate(this, ["refresh"])
27046         });
27047
27048         if(this.displayInfo){
27049             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27050         }
27051     },
27052
27053     // private
27054     updateInfo : function(){
27055         if(this.displayEl){
27056             var count = this.ds.getCount();
27057             var msg = count == 0 ?
27058                 this.emptyMsg :
27059                 String.format(
27060                     this.displayMsg,
27061                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27062                 );
27063             this.displayEl.update(msg);
27064         }
27065     },
27066
27067     // private
27068     onLoad : function(ds, r, o){
27069        this.cursor = o.params ? o.params.start : 0;
27070        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27071
27072        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27073        this.field.dom.value = ap;
27074        this.first.setDisabled(ap == 1);
27075        this.prev.setDisabled(ap == 1);
27076        this.next.setDisabled(ap == ps);
27077        this.last.setDisabled(ap == ps);
27078        this.loading.enable();
27079        this.updateInfo();
27080     },
27081
27082     // private
27083     getPageData : function(){
27084         var total = this.ds.getTotalCount();
27085         return {
27086             total : total,
27087             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27088             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27089         };
27090     },
27091
27092     // private
27093     onLoadError : function(){
27094         this.loading.enable();
27095     },
27096
27097     // private
27098     onPagingKeydown : function(e){
27099         var k = e.getKey();
27100         var d = this.getPageData();
27101         if(k == e.RETURN){
27102             var v = this.field.dom.value, pageNum;
27103             if(!v || isNaN(pageNum = parseInt(v, 10))){
27104                 this.field.dom.value = d.activePage;
27105                 return;
27106             }
27107             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27108             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27109             e.stopEvent();
27110         }
27111         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))
27112         {
27113           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27114           this.field.dom.value = pageNum;
27115           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27116           e.stopEvent();
27117         }
27118         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27119         {
27120           var v = this.field.dom.value, pageNum; 
27121           var increment = (e.shiftKey) ? 10 : 1;
27122           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27123             increment *= -1;
27124           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27125             this.field.dom.value = d.activePage;
27126             return;
27127           }
27128           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27129           {
27130             this.field.dom.value = parseInt(v, 10) + increment;
27131             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27132             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27133           }
27134           e.stopEvent();
27135         }
27136     },
27137
27138     // private
27139     beforeLoad : function(){
27140         if(this.loading){
27141             this.loading.disable();
27142         }
27143     },
27144
27145     // private
27146     onClick : function(which){
27147         var ds = this.ds;
27148         switch(which){
27149             case "first":
27150                 ds.load({params:{start: 0, limit: this.pageSize}});
27151             break;
27152             case "prev":
27153                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27154             break;
27155             case "next":
27156                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27157             break;
27158             case "last":
27159                 var total = ds.getTotalCount();
27160                 var extra = total % this.pageSize;
27161                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27162                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27163             break;
27164             case "refresh":
27165                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27166             break;
27167         }
27168     },
27169
27170     /**
27171      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27172      * @param {Roo.data.Store} store The data store to unbind
27173      */
27174     unbind : function(ds){
27175         ds.un("beforeload", this.beforeLoad, this);
27176         ds.un("load", this.onLoad, this);
27177         ds.un("loadexception", this.onLoadError, this);
27178         ds.un("remove", this.updateInfo, this);
27179         ds.un("add", this.updateInfo, this);
27180         this.ds = undefined;
27181     },
27182
27183     /**
27184      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27185      * @param {Roo.data.Store} store The data store to bind
27186      */
27187     bind : function(ds){
27188         ds.on("beforeload", this.beforeLoad, this);
27189         ds.on("load", this.onLoad, this);
27190         ds.on("loadexception", this.onLoadError, this);
27191         ds.on("remove", this.updateInfo, this);
27192         ds.on("add", this.updateInfo, this);
27193         this.ds = ds;
27194     }
27195 });/*
27196  * Based on:
27197  * Ext JS Library 1.1.1
27198  * Copyright(c) 2006-2007, Ext JS, LLC.
27199  *
27200  * Originally Released Under LGPL - original licence link has changed is not relivant.
27201  *
27202  * Fork - LGPL
27203  * <script type="text/javascript">
27204  */
27205
27206 /**
27207  * @class Roo.Resizable
27208  * @extends Roo.util.Observable
27209  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27210  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27211  * 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
27212  * the element will be wrapped for you automatically.</p>
27213  * <p>Here is the list of valid resize handles:</p>
27214  * <pre>
27215 Value   Description
27216 ------  -------------------
27217  'n'     north
27218  's'     south
27219  'e'     east
27220  'w'     west
27221  'nw'    northwest
27222  'sw'    southwest
27223  'se'    southeast
27224  'ne'    northeast
27225  'all'   all
27226 </pre>
27227  * <p>Here's an example showing the creation of a typical Resizable:</p>
27228  * <pre><code>
27229 var resizer = new Roo.Resizable("element-id", {
27230     handles: 'all',
27231     minWidth: 200,
27232     minHeight: 100,
27233     maxWidth: 500,
27234     maxHeight: 400,
27235     pinned: true
27236 });
27237 resizer.on("resize", myHandler);
27238 </code></pre>
27239  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27240  * resizer.east.setDisplayed(false);</p>
27241  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27242  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27243  * resize operation's new size (defaults to [0, 0])
27244  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27245  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27246  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27247  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27248  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27249  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27250  * @cfg {Number} width The width of the element in pixels (defaults to null)
27251  * @cfg {Number} height The height of the element in pixels (defaults to null)
27252  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27253  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27254  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27255  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27256  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27257  * in favor of the handles config option (defaults to false)
27258  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27259  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27260  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27261  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27262  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27263  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27264  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27265  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27266  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27267  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27268  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27269  * @constructor
27270  * Create a new resizable component
27271  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27272  * @param {Object} config configuration options
27273   */
27274 Roo.Resizable = function(el, config){
27275     this.el = Roo.get(el);
27276
27277     if(config && config.wrap){
27278         config.resizeChild = this.el;
27279         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27280         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27281         this.el.setStyle("overflow", "hidden");
27282         this.el.setPositioning(config.resizeChild.getPositioning());
27283         config.resizeChild.clearPositioning();
27284         if(!config.width || !config.height){
27285             var csize = config.resizeChild.getSize();
27286             this.el.setSize(csize.width, csize.height);
27287         }
27288         if(config.pinned && !config.adjustments){
27289             config.adjustments = "auto";
27290         }
27291     }
27292
27293     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27294     this.proxy.unselectable();
27295     this.proxy.enableDisplayMode('block');
27296
27297     Roo.apply(this, config);
27298
27299     if(this.pinned){
27300         this.disableTrackOver = true;
27301         this.el.addClass("x-resizable-pinned");
27302     }
27303     // if the element isn't positioned, make it relative
27304     var position = this.el.getStyle("position");
27305     if(position != "absolute" && position != "fixed"){
27306         this.el.setStyle("position", "relative");
27307     }
27308     if(!this.handles){ // no handles passed, must be legacy style
27309         this.handles = 's,e,se';
27310         if(this.multiDirectional){
27311             this.handles += ',n,w';
27312         }
27313     }
27314     if(this.handles == "all"){
27315         this.handles = "n s e w ne nw se sw";
27316     }
27317     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27318     var ps = Roo.Resizable.positions;
27319     for(var i = 0, len = hs.length; i < len; i++){
27320         if(hs[i] && ps[hs[i]]){
27321             var pos = ps[hs[i]];
27322             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27323         }
27324     }
27325     // legacy
27326     this.corner = this.southeast;
27327
27328     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27329         this.updateBox = true;
27330     }
27331
27332     this.activeHandle = null;
27333
27334     if(this.resizeChild){
27335         if(typeof this.resizeChild == "boolean"){
27336             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27337         }else{
27338             this.resizeChild = Roo.get(this.resizeChild, true);
27339         }
27340     }
27341
27342     if(this.adjustments == "auto"){
27343         var rc = this.resizeChild;
27344         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27345         if(rc && (hw || hn)){
27346             rc.position("relative");
27347             rc.setLeft(hw ? hw.el.getWidth() : 0);
27348             rc.setTop(hn ? hn.el.getHeight() : 0);
27349         }
27350         this.adjustments = [
27351             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27352             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27353         ];
27354     }
27355
27356     if(this.draggable){
27357         this.dd = this.dynamic ?
27358             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27359         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27360     }
27361
27362     // public events
27363     this.addEvents({
27364         /**
27365          * @event beforeresize
27366          * Fired before resize is allowed. Set enabled to false to cancel resize.
27367          * @param {Roo.Resizable} this
27368          * @param {Roo.EventObject} e The mousedown event
27369          */
27370         "beforeresize" : true,
27371         /**
27372          * @event resize
27373          * Fired after a resize.
27374          * @param {Roo.Resizable} this
27375          * @param {Number} width The new width
27376          * @param {Number} height The new height
27377          * @param {Roo.EventObject} e The mouseup event
27378          */
27379         "resize" : true
27380     });
27381
27382     if(this.width !== null && this.height !== null){
27383         this.resizeTo(this.width, this.height);
27384     }else{
27385         this.updateChildSize();
27386     }
27387     if(Roo.isIE){
27388         this.el.dom.style.zoom = 1;
27389     }
27390     Roo.Resizable.superclass.constructor.call(this);
27391 };
27392
27393 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27394         resizeChild : false,
27395         adjustments : [0, 0],
27396         minWidth : 5,
27397         minHeight : 5,
27398         maxWidth : 10000,
27399         maxHeight : 10000,
27400         enabled : true,
27401         animate : false,
27402         duration : .35,
27403         dynamic : false,
27404         handles : false,
27405         multiDirectional : false,
27406         disableTrackOver : false,
27407         easing : 'easeOutStrong',
27408         widthIncrement : 0,
27409         heightIncrement : 0,
27410         pinned : false,
27411         width : null,
27412         height : null,
27413         preserveRatio : false,
27414         transparent: false,
27415         minX: 0,
27416         minY: 0,
27417         draggable: false,
27418
27419         /**
27420          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27421          */
27422         constrainTo: undefined,
27423         /**
27424          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27425          */
27426         resizeRegion: undefined,
27427
27428
27429     /**
27430      * Perform a manual resize
27431      * @param {Number} width
27432      * @param {Number} height
27433      */
27434     resizeTo : function(width, height){
27435         this.el.setSize(width, height);
27436         this.updateChildSize();
27437         this.fireEvent("resize", this, width, height, null);
27438     },
27439
27440     // private
27441     startSizing : function(e, handle){
27442         this.fireEvent("beforeresize", this, e);
27443         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27444
27445             if(!this.overlay){
27446                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27447                 this.overlay.unselectable();
27448                 this.overlay.enableDisplayMode("block");
27449                 this.overlay.on("mousemove", this.onMouseMove, this);
27450                 this.overlay.on("mouseup", this.onMouseUp, this);
27451             }
27452             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27453
27454             this.resizing = true;
27455             this.startBox = this.el.getBox();
27456             this.startPoint = e.getXY();
27457             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27458                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27459
27460             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27461             this.overlay.show();
27462
27463             if(this.constrainTo) {
27464                 var ct = Roo.get(this.constrainTo);
27465                 this.resizeRegion = ct.getRegion().adjust(
27466                     ct.getFrameWidth('t'),
27467                     ct.getFrameWidth('l'),
27468                     -ct.getFrameWidth('b'),
27469                     -ct.getFrameWidth('r')
27470                 );
27471             }
27472
27473             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27474             this.proxy.show();
27475             this.proxy.setBox(this.startBox);
27476             if(!this.dynamic){
27477                 this.proxy.setStyle('visibility', 'visible');
27478             }
27479         }
27480     },
27481
27482     // private
27483     onMouseDown : function(handle, e){
27484         if(this.enabled){
27485             e.stopEvent();
27486             this.activeHandle = handle;
27487             this.startSizing(e, handle);
27488         }
27489     },
27490
27491     // private
27492     onMouseUp : function(e){
27493         var size = this.resizeElement();
27494         this.resizing = false;
27495         this.handleOut();
27496         this.overlay.hide();
27497         this.proxy.hide();
27498         this.fireEvent("resize", this, size.width, size.height, e);
27499     },
27500
27501     // private
27502     updateChildSize : function(){
27503         if(this.resizeChild){
27504             var el = this.el;
27505             var child = this.resizeChild;
27506             var adj = this.adjustments;
27507             if(el.dom.offsetWidth){
27508                 var b = el.getSize(true);
27509                 child.setSize(b.width+adj[0], b.height+adj[1]);
27510             }
27511             // Second call here for IE
27512             // The first call enables instant resizing and
27513             // the second call corrects scroll bars if they
27514             // exist
27515             if(Roo.isIE){
27516                 setTimeout(function(){
27517                     if(el.dom.offsetWidth){
27518                         var b = el.getSize(true);
27519                         child.setSize(b.width+adj[0], b.height+adj[1]);
27520                     }
27521                 }, 10);
27522             }
27523         }
27524     },
27525
27526     // private
27527     snap : function(value, inc, min){
27528         if(!inc || !value) return value;
27529         var newValue = value;
27530         var m = value % inc;
27531         if(m > 0){
27532             if(m > (inc/2)){
27533                 newValue = value + (inc-m);
27534             }else{
27535                 newValue = value - m;
27536             }
27537         }
27538         return Math.max(min, newValue);
27539     },
27540
27541     // private
27542     resizeElement : function(){
27543         var box = this.proxy.getBox();
27544         if(this.updateBox){
27545             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27546         }else{
27547             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27548         }
27549         this.updateChildSize();
27550         if(!this.dynamic){
27551             this.proxy.hide();
27552         }
27553         return box;
27554     },
27555
27556     // private
27557     constrain : function(v, diff, m, mx){
27558         if(v - diff < m){
27559             diff = v - m;
27560         }else if(v - diff > mx){
27561             diff = mx - v;
27562         }
27563         return diff;
27564     },
27565
27566     // private
27567     onMouseMove : function(e){
27568         if(this.enabled){
27569             try{// try catch so if something goes wrong the user doesn't get hung
27570
27571             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27572                 return;
27573             }
27574
27575             //var curXY = this.startPoint;
27576             var curSize = this.curSize || this.startBox;
27577             var x = this.startBox.x, y = this.startBox.y;
27578             var ox = x, oy = y;
27579             var w = curSize.width, h = curSize.height;
27580             var ow = w, oh = h;
27581             var mw = this.minWidth, mh = this.minHeight;
27582             var mxw = this.maxWidth, mxh = this.maxHeight;
27583             var wi = this.widthIncrement;
27584             var hi = this.heightIncrement;
27585
27586             var eventXY = e.getXY();
27587             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27588             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27589
27590             var pos = this.activeHandle.position;
27591
27592             switch(pos){
27593                 case "east":
27594                     w += diffX;
27595                     w = Math.min(Math.max(mw, w), mxw);
27596                     break;
27597                 case "south":
27598                     h += diffY;
27599                     h = Math.min(Math.max(mh, h), mxh);
27600                     break;
27601                 case "southeast":
27602                     w += diffX;
27603                     h += diffY;
27604                     w = Math.min(Math.max(mw, w), mxw);
27605                     h = Math.min(Math.max(mh, h), mxh);
27606                     break;
27607                 case "north":
27608                     diffY = this.constrain(h, diffY, mh, mxh);
27609                     y += diffY;
27610                     h -= diffY;
27611                     break;
27612                 case "west":
27613                     diffX = this.constrain(w, diffX, mw, mxw);
27614                     x += diffX;
27615                     w -= diffX;
27616                     break;
27617                 case "northeast":
27618                     w += diffX;
27619                     w = Math.min(Math.max(mw, w), mxw);
27620                     diffY = this.constrain(h, diffY, mh, mxh);
27621                     y += diffY;
27622                     h -= diffY;
27623                     break;
27624                 case "northwest":
27625                     diffX = this.constrain(w, diffX, mw, mxw);
27626                     diffY = this.constrain(h, diffY, mh, mxh);
27627                     y += diffY;
27628                     h -= diffY;
27629                     x += diffX;
27630                     w -= diffX;
27631                     break;
27632                case "southwest":
27633                     diffX = this.constrain(w, diffX, mw, mxw);
27634                     h += diffY;
27635                     h = Math.min(Math.max(mh, h), mxh);
27636                     x += diffX;
27637                     w -= diffX;
27638                     break;
27639             }
27640
27641             var sw = this.snap(w, wi, mw);
27642             var sh = this.snap(h, hi, mh);
27643             if(sw != w || sh != h){
27644                 switch(pos){
27645                     case "northeast":
27646                         y -= sh - h;
27647                     break;
27648                     case "north":
27649                         y -= sh - h;
27650                         break;
27651                     case "southwest":
27652                         x -= sw - w;
27653                     break;
27654                     case "west":
27655                         x -= sw - w;
27656                         break;
27657                     case "northwest":
27658                         x -= sw - w;
27659                         y -= sh - h;
27660                     break;
27661                 }
27662                 w = sw;
27663                 h = sh;
27664             }
27665
27666             if(this.preserveRatio){
27667                 switch(pos){
27668                     case "southeast":
27669                     case "east":
27670                         h = oh * (w/ow);
27671                         h = Math.min(Math.max(mh, h), mxh);
27672                         w = ow * (h/oh);
27673                        break;
27674                     case "south":
27675                         w = ow * (h/oh);
27676                         w = Math.min(Math.max(mw, w), mxw);
27677                         h = oh * (w/ow);
27678                         break;
27679                     case "northeast":
27680                         w = ow * (h/oh);
27681                         w = Math.min(Math.max(mw, w), mxw);
27682                         h = oh * (w/ow);
27683                     break;
27684                     case "north":
27685                         var tw = w;
27686                         w = ow * (h/oh);
27687                         w = Math.min(Math.max(mw, w), mxw);
27688                         h = oh * (w/ow);
27689                         x += (tw - w) / 2;
27690                         break;
27691                     case "southwest":
27692                         h = oh * (w/ow);
27693                         h = Math.min(Math.max(mh, h), mxh);
27694                         var tw = w;
27695                         w = ow * (h/oh);
27696                         x += tw - w;
27697                         break;
27698                     case "west":
27699                         var th = h;
27700                         h = oh * (w/ow);
27701                         h = Math.min(Math.max(mh, h), mxh);
27702                         y += (th - h) / 2;
27703                         var tw = w;
27704                         w = ow * (h/oh);
27705                         x += tw - w;
27706                        break;
27707                     case "northwest":
27708                         var tw = w;
27709                         var th = h;
27710                         h = oh * (w/ow);
27711                         h = Math.min(Math.max(mh, h), mxh);
27712                         w = ow * (h/oh);
27713                         y += th - h;
27714                          x += tw - w;
27715                        break;
27716
27717                 }
27718             }
27719             this.proxy.setBounds(x, y, w, h);
27720             if(this.dynamic){
27721                 this.resizeElement();
27722             }
27723             }catch(e){}
27724         }
27725     },
27726
27727     // private
27728     handleOver : function(){
27729         if(this.enabled){
27730             this.el.addClass("x-resizable-over");
27731         }
27732     },
27733
27734     // private
27735     handleOut : function(){
27736         if(!this.resizing){
27737             this.el.removeClass("x-resizable-over");
27738         }
27739     },
27740
27741     /**
27742      * Returns the element this component is bound to.
27743      * @return {Roo.Element}
27744      */
27745     getEl : function(){
27746         return this.el;
27747     },
27748
27749     /**
27750      * Returns the resizeChild element (or null).
27751      * @return {Roo.Element}
27752      */
27753     getResizeChild : function(){
27754         return this.resizeChild;
27755     },
27756
27757     /**
27758      * Destroys this resizable. If the element was wrapped and
27759      * removeEl is not true then the element remains.
27760      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27761      */
27762     destroy : function(removeEl){
27763         this.proxy.remove();
27764         if(this.overlay){
27765             this.overlay.removeAllListeners();
27766             this.overlay.remove();
27767         }
27768         var ps = Roo.Resizable.positions;
27769         for(var k in ps){
27770             if(typeof ps[k] != "function" && this[ps[k]]){
27771                 var h = this[ps[k]];
27772                 h.el.removeAllListeners();
27773                 h.el.remove();
27774             }
27775         }
27776         if(removeEl){
27777             this.el.update("");
27778             this.el.remove();
27779         }
27780     }
27781 });
27782
27783 // private
27784 // hash to map config positions to true positions
27785 Roo.Resizable.positions = {
27786     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27787 };
27788
27789 // private
27790 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27791     if(!this.tpl){
27792         // only initialize the template if resizable is used
27793         var tpl = Roo.DomHelper.createTemplate(
27794             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27795         );
27796         tpl.compile();
27797         Roo.Resizable.Handle.prototype.tpl = tpl;
27798     }
27799     this.position = pos;
27800     this.rz = rz;
27801     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27802     this.el.unselectable();
27803     if(transparent){
27804         this.el.setOpacity(0);
27805     }
27806     this.el.on("mousedown", this.onMouseDown, this);
27807     if(!disableTrackOver){
27808         this.el.on("mouseover", this.onMouseOver, this);
27809         this.el.on("mouseout", this.onMouseOut, this);
27810     }
27811 };
27812
27813 // private
27814 Roo.Resizable.Handle.prototype = {
27815     afterResize : function(rz){
27816         // do nothing
27817     },
27818     // private
27819     onMouseDown : function(e){
27820         this.rz.onMouseDown(this, e);
27821     },
27822     // private
27823     onMouseOver : function(e){
27824         this.rz.handleOver(this, e);
27825     },
27826     // private
27827     onMouseOut : function(e){
27828         this.rz.handleOut(this, e);
27829     }
27830 };/*
27831  * Based on:
27832  * Ext JS Library 1.1.1
27833  * Copyright(c) 2006-2007, Ext JS, LLC.
27834  *
27835  * Originally Released Under LGPL - original licence link has changed is not relivant.
27836  *
27837  * Fork - LGPL
27838  * <script type="text/javascript">
27839  */
27840
27841 /**
27842  * @class Roo.Editor
27843  * @extends Roo.Component
27844  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27845  * @constructor
27846  * Create a new Editor
27847  * @param {Roo.form.Field} field The Field object (or descendant)
27848  * @param {Object} config The config object
27849  */
27850 Roo.Editor = function(field, config){
27851     Roo.Editor.superclass.constructor.call(this, config);
27852     this.field = field;
27853     this.addEvents({
27854         /**
27855              * @event beforestartedit
27856              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27857              * false from the handler of this event.
27858              * @param {Editor} this
27859              * @param {Roo.Element} boundEl The underlying element bound to this editor
27860              * @param {Mixed} value The field value being set
27861              */
27862         "beforestartedit" : true,
27863         /**
27864              * @event startedit
27865              * Fires when this editor is displayed
27866              * @param {Roo.Element} boundEl The underlying element bound to this editor
27867              * @param {Mixed} value The starting field value
27868              */
27869         "startedit" : true,
27870         /**
27871              * @event beforecomplete
27872              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27873              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27874              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27875              * event will not fire since no edit actually occurred.
27876              * @param {Editor} this
27877              * @param {Mixed} value The current field value
27878              * @param {Mixed} startValue The original field value
27879              */
27880         "beforecomplete" : true,
27881         /**
27882              * @event complete
27883              * Fires after editing is complete and any changed value has been written to the underlying field.
27884              * @param {Editor} this
27885              * @param {Mixed} value The current field value
27886              * @param {Mixed} startValue The original field value
27887              */
27888         "complete" : true,
27889         /**
27890          * @event specialkey
27891          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27892          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27893          * @param {Roo.form.Field} this
27894          * @param {Roo.EventObject} e The event object
27895          */
27896         "specialkey" : true
27897     });
27898 };
27899
27900 Roo.extend(Roo.Editor, Roo.Component, {
27901     /**
27902      * @cfg {Boolean/String} autosize
27903      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27904      * or "height" to adopt the height only (defaults to false)
27905      */
27906     /**
27907      * @cfg {Boolean} revertInvalid
27908      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27909      * validation fails (defaults to true)
27910      */
27911     /**
27912      * @cfg {Boolean} ignoreNoChange
27913      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27914      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27915      * will never be ignored.
27916      */
27917     /**
27918      * @cfg {Boolean} hideEl
27919      * False to keep the bound element visible while the editor is displayed (defaults to true)
27920      */
27921     /**
27922      * @cfg {Mixed} value
27923      * The data value of the underlying field (defaults to "")
27924      */
27925     value : "",
27926     /**
27927      * @cfg {String} alignment
27928      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27929      */
27930     alignment: "c-c?",
27931     /**
27932      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27933      * for bottom-right shadow (defaults to "frame")
27934      */
27935     shadow : "frame",
27936     /**
27937      * @cfg {Boolean} constrain True to constrain the editor to the viewport
27938      */
27939     constrain : false,
27940     /**
27941      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
27942      */
27943     completeOnEnter : false,
27944     /**
27945      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
27946      */
27947     cancelOnEsc : false,
27948     /**
27949      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
27950      */
27951     updateEl : false,
27952
27953     // private
27954     onRender : function(ct, position){
27955         this.el = new Roo.Layer({
27956             shadow: this.shadow,
27957             cls: "x-editor",
27958             parentEl : ct,
27959             shim : this.shim,
27960             shadowOffset:4,
27961             id: this.id,
27962             constrain: this.constrain
27963         });
27964         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
27965         if(this.field.msgTarget != 'title'){
27966             this.field.msgTarget = 'qtip';
27967         }
27968         this.field.render(this.el);
27969         if(Roo.isGecko){
27970             this.field.el.dom.setAttribute('autocomplete', 'off');
27971         }
27972         this.field.on("specialkey", this.onSpecialKey, this);
27973         if(this.swallowKeys){
27974             this.field.el.swallowEvent(['keydown','keypress']);
27975         }
27976         this.field.show();
27977         this.field.on("blur", this.onBlur, this);
27978         if(this.field.grow){
27979             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
27980         }
27981     },
27982
27983     onSpecialKey : function(field, e){
27984         if(this.completeOnEnter && e.getKey() == e.ENTER){
27985             e.stopEvent();
27986             this.completeEdit();
27987         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
27988             this.cancelEdit();
27989         }else{
27990             this.fireEvent('specialkey', field, e);
27991         }
27992     },
27993
27994     /**
27995      * Starts the editing process and shows the editor.
27996      * @param {String/HTMLElement/Element} el The element to edit
27997      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
27998       * to the innerHTML of el.
27999      */
28000     startEdit : function(el, value){
28001         if(this.editing){
28002             this.completeEdit();
28003         }
28004         this.boundEl = Roo.get(el);
28005         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28006         if(!this.rendered){
28007             this.render(this.parentEl || document.body);
28008         }
28009         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28010             return;
28011         }
28012         this.startValue = v;
28013         this.field.setValue(v);
28014         if(this.autoSize){
28015             var sz = this.boundEl.getSize();
28016             switch(this.autoSize){
28017                 case "width":
28018                 this.setSize(sz.width,  "");
28019                 break;
28020                 case "height":
28021                 this.setSize("",  sz.height);
28022                 break;
28023                 default:
28024                 this.setSize(sz.width,  sz.height);
28025             }
28026         }
28027         this.el.alignTo(this.boundEl, this.alignment);
28028         this.editing = true;
28029         if(Roo.QuickTips){
28030             Roo.QuickTips.disable();
28031         }
28032         this.show();
28033     },
28034
28035     /**
28036      * Sets the height and width of this editor.
28037      * @param {Number} width The new width
28038      * @param {Number} height The new height
28039      */
28040     setSize : function(w, h){
28041         this.field.setSize(w, h);
28042         if(this.el){
28043             this.el.sync();
28044         }
28045     },
28046
28047     /**
28048      * Realigns the editor to the bound field based on the current alignment config value.
28049      */
28050     realign : function(){
28051         this.el.alignTo(this.boundEl, this.alignment);
28052     },
28053
28054     /**
28055      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28056      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28057      */
28058     completeEdit : function(remainVisible){
28059         if(!this.editing){
28060             return;
28061         }
28062         var v = this.getValue();
28063         if(this.revertInvalid !== false && !this.field.isValid()){
28064             v = this.startValue;
28065             this.cancelEdit(true);
28066         }
28067         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28068             this.editing = false;
28069             this.hide();
28070             return;
28071         }
28072         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28073             this.editing = false;
28074             if(this.updateEl && this.boundEl){
28075                 this.boundEl.update(v);
28076             }
28077             if(remainVisible !== true){
28078                 this.hide();
28079             }
28080             this.fireEvent("complete", this, v, this.startValue);
28081         }
28082     },
28083
28084     // private
28085     onShow : function(){
28086         this.el.show();
28087         if(this.hideEl !== false){
28088             this.boundEl.hide();
28089         }
28090         this.field.show();
28091         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28092             this.fixIEFocus = true;
28093             this.deferredFocus.defer(50, this);
28094         }else{
28095             this.field.focus();
28096         }
28097         this.fireEvent("startedit", this.boundEl, this.startValue);
28098     },
28099
28100     deferredFocus : function(){
28101         if(this.editing){
28102             this.field.focus();
28103         }
28104     },
28105
28106     /**
28107      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28108      * reverted to the original starting value.
28109      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28110      * cancel (defaults to false)
28111      */
28112     cancelEdit : function(remainVisible){
28113         if(this.editing){
28114             this.setValue(this.startValue);
28115             if(remainVisible !== true){
28116                 this.hide();
28117             }
28118         }
28119     },
28120
28121     // private
28122     onBlur : function(){
28123         if(this.allowBlur !== true && this.editing){
28124             this.completeEdit();
28125         }
28126     },
28127
28128     // private
28129     onHide : function(){
28130         if(this.editing){
28131             this.completeEdit();
28132             return;
28133         }
28134         this.field.blur();
28135         if(this.field.collapse){
28136             this.field.collapse();
28137         }
28138         this.el.hide();
28139         if(this.hideEl !== false){
28140             this.boundEl.show();
28141         }
28142         if(Roo.QuickTips){
28143             Roo.QuickTips.enable();
28144         }
28145     },
28146
28147     /**
28148      * Sets the data value of the editor
28149      * @param {Mixed} value Any valid value supported by the underlying field
28150      */
28151     setValue : function(v){
28152         this.field.setValue(v);
28153     },
28154
28155     /**
28156      * Gets the data value of the editor
28157      * @return {Mixed} The data value
28158      */
28159     getValue : function(){
28160         return this.field.getValue();
28161     }
28162 });/*
28163  * Based on:
28164  * Ext JS Library 1.1.1
28165  * Copyright(c) 2006-2007, Ext JS, LLC.
28166  *
28167  * Originally Released Under LGPL - original licence link has changed is not relivant.
28168  *
28169  * Fork - LGPL
28170  * <script type="text/javascript">
28171  */
28172  
28173 /**
28174  * @class Roo.BasicDialog
28175  * @extends Roo.util.Observable
28176  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28177  * <pre><code>
28178 var dlg = new Roo.BasicDialog("my-dlg", {
28179     height: 200,
28180     width: 300,
28181     minHeight: 100,
28182     minWidth: 150,
28183     modal: true,
28184     proxyDrag: true,
28185     shadow: true
28186 });
28187 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28188 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28189 dlg.addButton('Cancel', dlg.hide, dlg);
28190 dlg.show();
28191 </code></pre>
28192   <b>A Dialog should always be a direct child of the body element.</b>
28193  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28194  * @cfg {String} title Default text to display in the title bar (defaults to null)
28195  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28196  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28197  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28198  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28199  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28200  * (defaults to null with no animation)
28201  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28202  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28203  * property for valid values (defaults to 'all')
28204  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28205  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28206  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28207  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28208  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28209  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28210  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28211  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28212  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28213  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28214  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28215  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28216  * draggable = true (defaults to false)
28217  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28218  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28219  * shadow (defaults to false)
28220  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28221  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28222  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28223  * @cfg {Array} buttons Array of buttons
28224  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28225  * @constructor
28226  * Create a new BasicDialog.
28227  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28228  * @param {Object} config Configuration options
28229  */
28230 Roo.BasicDialog = function(el, config){
28231     this.el = Roo.get(el);
28232     var dh = Roo.DomHelper;
28233     if(!this.el && config && config.autoCreate){
28234         if(typeof config.autoCreate == "object"){
28235             if(!config.autoCreate.id){
28236                 config.autoCreate.id = el;
28237             }
28238             this.el = dh.append(document.body,
28239                         config.autoCreate, true);
28240         }else{
28241             this.el = dh.append(document.body,
28242                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28243         }
28244     }
28245     el = this.el;
28246     el.setDisplayed(true);
28247     el.hide = this.hideAction;
28248     this.id = el.id;
28249     el.addClass("x-dlg");
28250
28251     Roo.apply(this, config);
28252
28253     this.proxy = el.createProxy("x-dlg-proxy");
28254     this.proxy.hide = this.hideAction;
28255     this.proxy.setOpacity(.5);
28256     this.proxy.hide();
28257
28258     if(config.width){
28259         el.setWidth(config.width);
28260     }
28261     if(config.height){
28262         el.setHeight(config.height);
28263     }
28264     this.size = el.getSize();
28265     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28266         this.xy = [config.x,config.y];
28267     }else{
28268         this.xy = el.getCenterXY(true);
28269     }
28270     /** The header element @type Roo.Element */
28271     this.header = el.child("> .x-dlg-hd");
28272     /** The body element @type Roo.Element */
28273     this.body = el.child("> .x-dlg-bd");
28274     /** The footer element @type Roo.Element */
28275     this.footer = el.child("> .x-dlg-ft");
28276
28277     if(!this.header){
28278         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28279     }
28280     if(!this.body){
28281         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28282     }
28283
28284     this.header.unselectable();
28285     if(this.title){
28286         this.header.update(this.title);
28287     }
28288     // this element allows the dialog to be focused for keyboard event
28289     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28290     this.focusEl.swallowEvent("click", true);
28291
28292     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28293
28294     // wrap the body and footer for special rendering
28295     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28296     if(this.footer){
28297         this.bwrap.dom.appendChild(this.footer.dom);
28298     }
28299
28300     this.bg = this.el.createChild({
28301         tag: "div", cls:"x-dlg-bg",
28302         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28303     });
28304     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28305
28306
28307     if(this.autoScroll !== false && !this.autoTabs){
28308         this.body.setStyle("overflow", "auto");
28309     }
28310
28311     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28312
28313     if(this.closable !== false){
28314         this.el.addClass("x-dlg-closable");
28315         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28316         this.close.on("click", this.closeClick, this);
28317         this.close.addClassOnOver("x-dlg-close-over");
28318     }
28319     if(this.collapsible !== false){
28320         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28321         this.collapseBtn.on("click", this.collapseClick, this);
28322         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28323         this.header.on("dblclick", this.collapseClick, this);
28324     }
28325     if(this.resizable !== false){
28326         this.el.addClass("x-dlg-resizable");
28327         this.resizer = new Roo.Resizable(el, {
28328             minWidth: this.minWidth || 80,
28329             minHeight:this.minHeight || 80,
28330             handles: this.resizeHandles || "all",
28331             pinned: true
28332         });
28333         this.resizer.on("beforeresize", this.beforeResize, this);
28334         this.resizer.on("resize", this.onResize, this);
28335     }
28336     if(this.draggable !== false){
28337         el.addClass("x-dlg-draggable");
28338         if (!this.proxyDrag) {
28339             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28340         }
28341         else {
28342             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28343         }
28344         dd.setHandleElId(this.header.id);
28345         dd.endDrag = this.endMove.createDelegate(this);
28346         dd.startDrag = this.startMove.createDelegate(this);
28347         dd.onDrag = this.onDrag.createDelegate(this);
28348         dd.scroll = false;
28349         this.dd = dd;
28350     }
28351     if(this.modal){
28352         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28353         this.mask.enableDisplayMode("block");
28354         this.mask.hide();
28355         this.el.addClass("x-dlg-modal");
28356     }
28357     if(this.shadow){
28358         this.shadow = new Roo.Shadow({
28359             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28360             offset : this.shadowOffset
28361         });
28362     }else{
28363         this.shadowOffset = 0;
28364     }
28365     if(Roo.useShims && this.shim !== false){
28366         this.shim = this.el.createShim();
28367         this.shim.hide = this.hideAction;
28368         this.shim.hide();
28369     }else{
28370         this.shim = false;
28371     }
28372     if(this.autoTabs){
28373         this.initTabs();
28374     }
28375     if (this.buttons) { 
28376         var bts= this.buttons;
28377         this.buttons = [];
28378         Roo.each(bts, function(b) {
28379             this.addButton(b);
28380         }, this);
28381     }
28382     
28383     
28384     this.addEvents({
28385         /**
28386          * @event keydown
28387          * Fires when a key is pressed
28388          * @param {Roo.BasicDialog} this
28389          * @param {Roo.EventObject} e
28390          */
28391         "keydown" : true,
28392         /**
28393          * @event move
28394          * Fires when this dialog is moved by the user.
28395          * @param {Roo.BasicDialog} this
28396          * @param {Number} x The new page X
28397          * @param {Number} y The new page Y
28398          */
28399         "move" : true,
28400         /**
28401          * @event resize
28402          * Fires when this dialog is resized by the user.
28403          * @param {Roo.BasicDialog} this
28404          * @param {Number} width The new width
28405          * @param {Number} height The new height
28406          */
28407         "resize" : true,
28408         /**
28409          * @event beforehide
28410          * Fires before this dialog is hidden.
28411          * @param {Roo.BasicDialog} this
28412          */
28413         "beforehide" : true,
28414         /**
28415          * @event hide
28416          * Fires when this dialog is hidden.
28417          * @param {Roo.BasicDialog} this
28418          */
28419         "hide" : true,
28420         /**
28421          * @event beforeshow
28422          * Fires before this dialog is shown.
28423          * @param {Roo.BasicDialog} this
28424          */
28425         "beforeshow" : true,
28426         /**
28427          * @event show
28428          * Fires when this dialog is shown.
28429          * @param {Roo.BasicDialog} this
28430          */
28431         "show" : true
28432     });
28433     el.on("keydown", this.onKeyDown, this);
28434     el.on("mousedown", this.toFront, this);
28435     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28436     this.el.hide();
28437     Roo.DialogManager.register(this);
28438     Roo.BasicDialog.superclass.constructor.call(this);
28439 };
28440
28441 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28442     shadowOffset: Roo.isIE ? 6 : 5,
28443     minHeight: 80,
28444     minWidth: 200,
28445     minButtonWidth: 75,
28446     defaultButton: null,
28447     buttonAlign: "right",
28448     tabTag: 'div',
28449     firstShow: true,
28450
28451     /**
28452      * Sets the dialog title text
28453      * @param {String} text The title text to display
28454      * @return {Roo.BasicDialog} this
28455      */
28456     setTitle : function(text){
28457         this.header.update(text);
28458         return this;
28459     },
28460
28461     // private
28462     closeClick : function(){
28463         this.hide();
28464     },
28465
28466     // private
28467     collapseClick : function(){
28468         this[this.collapsed ? "expand" : "collapse"]();
28469     },
28470
28471     /**
28472      * Collapses the dialog to its minimized state (only the title bar is visible).
28473      * Equivalent to the user clicking the collapse dialog button.
28474      */
28475     collapse : function(){
28476         if(!this.collapsed){
28477             this.collapsed = true;
28478             this.el.addClass("x-dlg-collapsed");
28479             this.restoreHeight = this.el.getHeight();
28480             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28481         }
28482     },
28483
28484     /**
28485      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28486      * clicking the expand dialog button.
28487      */
28488     expand : function(){
28489         if(this.collapsed){
28490             this.collapsed = false;
28491             this.el.removeClass("x-dlg-collapsed");
28492             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28493         }
28494     },
28495
28496     /**
28497      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28498      * @return {Roo.TabPanel} The tabs component
28499      */
28500     initTabs : function(){
28501         var tabs = this.getTabs();
28502         while(tabs.getTab(0)){
28503             tabs.removeTab(0);
28504         }
28505         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28506             var dom = el.dom;
28507             tabs.addTab(Roo.id(dom), dom.title);
28508             dom.title = "";
28509         });
28510         tabs.activate(0);
28511         return tabs;
28512     },
28513
28514     // private
28515     beforeResize : function(){
28516         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28517     },
28518
28519     // private
28520     onResize : function(){
28521         this.refreshSize();
28522         this.syncBodyHeight();
28523         this.adjustAssets();
28524         this.focus();
28525         this.fireEvent("resize", this, this.size.width, this.size.height);
28526     },
28527
28528     // private
28529     onKeyDown : function(e){
28530         if(this.isVisible()){
28531             this.fireEvent("keydown", this, e);
28532         }
28533     },
28534
28535     /**
28536      * Resizes the dialog.
28537      * @param {Number} width
28538      * @param {Number} height
28539      * @return {Roo.BasicDialog} this
28540      */
28541     resizeTo : function(width, height){
28542         this.el.setSize(width, height);
28543         this.size = {width: width, height: height};
28544         this.syncBodyHeight();
28545         if(this.fixedcenter){
28546             this.center();
28547         }
28548         if(this.isVisible()){
28549             this.constrainXY();
28550             this.adjustAssets();
28551         }
28552         this.fireEvent("resize", this, width, height);
28553         return this;
28554     },
28555
28556
28557     /**
28558      * Resizes the dialog to fit the specified content size.
28559      * @param {Number} width
28560      * @param {Number} height
28561      * @return {Roo.BasicDialog} this
28562      */
28563     setContentSize : function(w, h){
28564         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28565         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28566         //if(!this.el.isBorderBox()){
28567             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28568             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28569         //}
28570         if(this.tabs){
28571             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28572             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28573         }
28574         this.resizeTo(w, h);
28575         return this;
28576     },
28577
28578     /**
28579      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28580      * executed in response to a particular key being pressed while the dialog is active.
28581      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28582      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28583      * @param {Function} fn The function to call
28584      * @param {Object} scope (optional) The scope of the function
28585      * @return {Roo.BasicDialog} this
28586      */
28587     addKeyListener : function(key, fn, scope){
28588         var keyCode, shift, ctrl, alt;
28589         if(typeof key == "object" && !(key instanceof Array)){
28590             keyCode = key["key"];
28591             shift = key["shift"];
28592             ctrl = key["ctrl"];
28593             alt = key["alt"];
28594         }else{
28595             keyCode = key;
28596         }
28597         var handler = function(dlg, e){
28598             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28599                 var k = e.getKey();
28600                 if(keyCode instanceof Array){
28601                     for(var i = 0, len = keyCode.length; i < len; i++){
28602                         if(keyCode[i] == k){
28603                           fn.call(scope || window, dlg, k, e);
28604                           return;
28605                         }
28606                     }
28607                 }else{
28608                     if(k == keyCode){
28609                         fn.call(scope || window, dlg, k, e);
28610                     }
28611                 }
28612             }
28613         };
28614         this.on("keydown", handler);
28615         return this;
28616     },
28617
28618     /**
28619      * Returns the TabPanel component (creates it if it doesn't exist).
28620      * Note: If you wish to simply check for the existence of tabs without creating them,
28621      * check for a null 'tabs' property.
28622      * @return {Roo.TabPanel} The tabs component
28623      */
28624     getTabs : function(){
28625         if(!this.tabs){
28626             this.el.addClass("x-dlg-auto-tabs");
28627             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28628             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28629         }
28630         return this.tabs;
28631     },
28632
28633     /**
28634      * Adds a button to the footer section of the dialog.
28635      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28636      * object or a valid Roo.DomHelper element config
28637      * @param {Function} handler The function called when the button is clicked
28638      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28639      * @return {Roo.Button} The new button
28640      */
28641     addButton : function(config, handler, scope){
28642         var dh = Roo.DomHelper;
28643         if(!this.footer){
28644             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28645         }
28646         if(!this.btnContainer){
28647             var tb = this.footer.createChild({
28648
28649                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28650                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28651             }, null, true);
28652             this.btnContainer = tb.firstChild.firstChild.firstChild;
28653         }
28654         var bconfig = {
28655             handler: handler,
28656             scope: scope,
28657             minWidth: this.minButtonWidth,
28658             hideParent:true
28659         };
28660         if(typeof config == "string"){
28661             bconfig.text = config;
28662         }else{
28663             if(config.tag){
28664                 bconfig.dhconfig = config;
28665             }else{
28666                 Roo.apply(bconfig, config);
28667             }
28668         }
28669         var fc = false;
28670         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28671             bconfig.position = Math.max(0, bconfig.position);
28672             fc = this.btnContainer.childNodes[bconfig.position];
28673         }
28674          
28675         var btn = new Roo.Button(
28676             fc ? 
28677                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28678                 : this.btnContainer.appendChild(document.createElement("td")),
28679             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28680             bconfig
28681         );
28682         this.syncBodyHeight();
28683         if(!this.buttons){
28684             /**
28685              * Array of all the buttons that have been added to this dialog via addButton
28686              * @type Array
28687              */
28688             this.buttons = [];
28689         }
28690         this.buttons.push(btn);
28691         return btn;
28692     },
28693
28694     /**
28695      * Sets the default button to be focused when the dialog is displayed.
28696      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28697      * @return {Roo.BasicDialog} this
28698      */
28699     setDefaultButton : function(btn){
28700         this.defaultButton = btn;
28701         return this;
28702     },
28703
28704     // private
28705     getHeaderFooterHeight : function(safe){
28706         var height = 0;
28707         if(this.header){
28708            height += this.header.getHeight();
28709         }
28710         if(this.footer){
28711            var fm = this.footer.getMargins();
28712             height += (this.footer.getHeight()+fm.top+fm.bottom);
28713         }
28714         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28715         height += this.centerBg.getPadding("tb");
28716         return height;
28717     },
28718
28719     // private
28720     syncBodyHeight : function(){
28721         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28722         var height = this.size.height - this.getHeaderFooterHeight(false);
28723         bd.setHeight(height-bd.getMargins("tb"));
28724         var hh = this.header.getHeight();
28725         var h = this.size.height-hh;
28726         cb.setHeight(h);
28727         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28728         bw.setHeight(h-cb.getPadding("tb"));
28729         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28730         bd.setWidth(bw.getWidth(true));
28731         if(this.tabs){
28732             this.tabs.syncHeight();
28733             if(Roo.isIE){
28734                 this.tabs.el.repaint();
28735             }
28736         }
28737     },
28738
28739     /**
28740      * Restores the previous state of the dialog if Roo.state is configured.
28741      * @return {Roo.BasicDialog} this
28742      */
28743     restoreState : function(){
28744         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28745         if(box && box.width){
28746             this.xy = [box.x, box.y];
28747             this.resizeTo(box.width, box.height);
28748         }
28749         return this;
28750     },
28751
28752     // private
28753     beforeShow : function(){
28754         this.expand();
28755         if(this.fixedcenter){
28756             this.xy = this.el.getCenterXY(true);
28757         }
28758         if(this.modal){
28759             Roo.get(document.body).addClass("x-body-masked");
28760             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28761             this.mask.show();
28762         }
28763         this.constrainXY();
28764     },
28765
28766     // private
28767     animShow : function(){
28768         var b = Roo.get(this.animateTarget, true).getBox();
28769         this.proxy.setSize(b.width, b.height);
28770         this.proxy.setLocation(b.x, b.y);
28771         this.proxy.show();
28772         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28773                     true, .35, this.showEl.createDelegate(this));
28774     },
28775
28776     /**
28777      * Shows the dialog.
28778      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28779      * @return {Roo.BasicDialog} this
28780      */
28781     show : function(animateTarget){
28782         if (this.fireEvent("beforeshow", this) === false){
28783             return;
28784         }
28785         if(this.syncHeightBeforeShow){
28786             this.syncBodyHeight();
28787         }else if(this.firstShow){
28788             this.firstShow = false;
28789             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28790         }
28791         this.animateTarget = animateTarget || this.animateTarget;
28792         if(!this.el.isVisible()){
28793             this.beforeShow();
28794             if(this.animateTarget){
28795                 this.animShow();
28796             }else{
28797                 this.showEl();
28798             }
28799         }
28800         return this;
28801     },
28802
28803     // private
28804     showEl : function(){
28805         this.proxy.hide();
28806         this.el.setXY(this.xy);
28807         this.el.show();
28808         this.adjustAssets(true);
28809         this.toFront();
28810         this.focus();
28811         // IE peekaboo bug - fix found by Dave Fenwick
28812         if(Roo.isIE){
28813             this.el.repaint();
28814         }
28815         this.fireEvent("show", this);
28816     },
28817
28818     /**
28819      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28820      * dialog itself will receive focus.
28821      */
28822     focus : function(){
28823         if(this.defaultButton){
28824             this.defaultButton.focus();
28825         }else{
28826             this.focusEl.focus();
28827         }
28828     },
28829
28830     // private
28831     constrainXY : function(){
28832         if(this.constraintoviewport !== false){
28833             if(!this.viewSize){
28834                 if(this.container){
28835                     var s = this.container.getSize();
28836                     this.viewSize = [s.width, s.height];
28837                 }else{
28838                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28839                 }
28840             }
28841             var s = Roo.get(this.container||document).getScroll();
28842
28843             var x = this.xy[0], y = this.xy[1];
28844             var w = this.size.width, h = this.size.height;
28845             var vw = this.viewSize[0], vh = this.viewSize[1];
28846             // only move it if it needs it
28847             var moved = false;
28848             // first validate right/bottom
28849             if(x + w > vw+s.left){
28850                 x = vw - w;
28851                 moved = true;
28852             }
28853             if(y + h > vh+s.top){
28854                 y = vh - h;
28855                 moved = true;
28856             }
28857             // then make sure top/left isn't negative
28858             if(x < s.left){
28859                 x = s.left;
28860                 moved = true;
28861             }
28862             if(y < s.top){
28863                 y = s.top;
28864                 moved = true;
28865             }
28866             if(moved){
28867                 // cache xy
28868                 this.xy = [x, y];
28869                 if(this.isVisible()){
28870                     this.el.setLocation(x, y);
28871                     this.adjustAssets();
28872                 }
28873             }
28874         }
28875     },
28876
28877     // private
28878     onDrag : function(){
28879         if(!this.proxyDrag){
28880             this.xy = this.el.getXY();
28881             this.adjustAssets();
28882         }
28883     },
28884
28885     // private
28886     adjustAssets : function(doShow){
28887         var x = this.xy[0], y = this.xy[1];
28888         var w = this.size.width, h = this.size.height;
28889         if(doShow === true){
28890             if(this.shadow){
28891                 this.shadow.show(this.el);
28892             }
28893             if(this.shim){
28894                 this.shim.show();
28895             }
28896         }
28897         if(this.shadow && this.shadow.isVisible()){
28898             this.shadow.show(this.el);
28899         }
28900         if(this.shim && this.shim.isVisible()){
28901             this.shim.setBounds(x, y, w, h);
28902         }
28903     },
28904
28905     // private
28906     adjustViewport : function(w, h){
28907         if(!w || !h){
28908             w = Roo.lib.Dom.getViewWidth();
28909             h = Roo.lib.Dom.getViewHeight();
28910         }
28911         // cache the size
28912         this.viewSize = [w, h];
28913         if(this.modal && this.mask.isVisible()){
28914             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28915             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28916         }
28917         if(this.isVisible()){
28918             this.constrainXY();
28919         }
28920     },
28921
28922     /**
28923      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28924      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28925      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28926      */
28927     destroy : function(removeEl){
28928         if(this.isVisible()){
28929             this.animateTarget = null;
28930             this.hide();
28931         }
28932         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28933         if(this.tabs){
28934             this.tabs.destroy(removeEl);
28935         }
28936         Roo.destroy(
28937              this.shim,
28938              this.proxy,
28939              this.resizer,
28940              this.close,
28941              this.mask
28942         );
28943         if(this.dd){
28944             this.dd.unreg();
28945         }
28946         if(this.buttons){
28947            for(var i = 0, len = this.buttons.length; i < len; i++){
28948                this.buttons[i].destroy();
28949            }
28950         }
28951         this.el.removeAllListeners();
28952         if(removeEl === true){
28953             this.el.update("");
28954             this.el.remove();
28955         }
28956         Roo.DialogManager.unregister(this);
28957     },
28958
28959     // private
28960     startMove : function(){
28961         if(this.proxyDrag){
28962             this.proxy.show();
28963         }
28964         if(this.constraintoviewport !== false){
28965             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
28966         }
28967     },
28968
28969     // private
28970     endMove : function(){
28971         if(!this.proxyDrag){
28972             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
28973         }else{
28974             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
28975             this.proxy.hide();
28976         }
28977         this.refreshSize();
28978         this.adjustAssets();
28979         this.focus();
28980         this.fireEvent("move", this, this.xy[0], this.xy[1]);
28981     },
28982
28983     /**
28984      * Brings this dialog to the front of any other visible dialogs
28985      * @return {Roo.BasicDialog} this
28986      */
28987     toFront : function(){
28988         Roo.DialogManager.bringToFront(this);
28989         return this;
28990     },
28991
28992     /**
28993      * Sends this dialog to the back (under) of any other visible dialogs
28994      * @return {Roo.BasicDialog} this
28995      */
28996     toBack : function(){
28997         Roo.DialogManager.sendToBack(this);
28998         return this;
28999     },
29000
29001     /**
29002      * Centers this dialog in the viewport
29003      * @return {Roo.BasicDialog} this
29004      */
29005     center : function(){
29006         var xy = this.el.getCenterXY(true);
29007         this.moveTo(xy[0], xy[1]);
29008         return this;
29009     },
29010
29011     /**
29012      * Moves the dialog's top-left corner to the specified point
29013      * @param {Number} x
29014      * @param {Number} y
29015      * @return {Roo.BasicDialog} this
29016      */
29017     moveTo : function(x, y){
29018         this.xy = [x,y];
29019         if(this.isVisible()){
29020             this.el.setXY(this.xy);
29021             this.adjustAssets();
29022         }
29023         return this;
29024     },
29025
29026     /**
29027      * Aligns the dialog to the specified element
29028      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29029      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29030      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29031      * @return {Roo.BasicDialog} this
29032      */
29033     alignTo : function(element, position, offsets){
29034         this.xy = this.el.getAlignToXY(element, position, offsets);
29035         if(this.isVisible()){
29036             this.el.setXY(this.xy);
29037             this.adjustAssets();
29038         }
29039         return this;
29040     },
29041
29042     /**
29043      * Anchors an element to another element and realigns it when the window is resized.
29044      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29045      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29046      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29047      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29048      * is a number, it is used as the buffer delay (defaults to 50ms).
29049      * @return {Roo.BasicDialog} this
29050      */
29051     anchorTo : function(el, alignment, offsets, monitorScroll){
29052         var action = function(){
29053             this.alignTo(el, alignment, offsets);
29054         };
29055         Roo.EventManager.onWindowResize(action, this);
29056         var tm = typeof monitorScroll;
29057         if(tm != 'undefined'){
29058             Roo.EventManager.on(window, 'scroll', action, this,
29059                 {buffer: tm == 'number' ? monitorScroll : 50});
29060         }
29061         action.call(this);
29062         return this;
29063     },
29064
29065     /**
29066      * Returns true if the dialog is visible
29067      * @return {Boolean}
29068      */
29069     isVisible : function(){
29070         return this.el.isVisible();
29071     },
29072
29073     // private
29074     animHide : function(callback){
29075         var b = Roo.get(this.animateTarget).getBox();
29076         this.proxy.show();
29077         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29078         this.el.hide();
29079         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29080                     this.hideEl.createDelegate(this, [callback]));
29081     },
29082
29083     /**
29084      * Hides the dialog.
29085      * @param {Function} callback (optional) Function to call when the dialog is hidden
29086      * @return {Roo.BasicDialog} this
29087      */
29088     hide : function(callback){
29089         if (this.fireEvent("beforehide", this) === false){
29090             return;
29091         }
29092         if(this.shadow){
29093             this.shadow.hide();
29094         }
29095         if(this.shim) {
29096           this.shim.hide();
29097         }
29098         if(this.animateTarget){
29099            this.animHide(callback);
29100         }else{
29101             this.el.hide();
29102             this.hideEl(callback);
29103         }
29104         return this;
29105     },
29106
29107     // private
29108     hideEl : function(callback){
29109         this.proxy.hide();
29110         if(this.modal){
29111             this.mask.hide();
29112             Roo.get(document.body).removeClass("x-body-masked");
29113         }
29114         this.fireEvent("hide", this);
29115         if(typeof callback == "function"){
29116             callback();
29117         }
29118     },
29119
29120     // private
29121     hideAction : function(){
29122         this.setLeft("-10000px");
29123         this.setTop("-10000px");
29124         this.setStyle("visibility", "hidden");
29125     },
29126
29127     // private
29128     refreshSize : function(){
29129         this.size = this.el.getSize();
29130         this.xy = this.el.getXY();
29131         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29132     },
29133
29134     // private
29135     // z-index is managed by the DialogManager and may be overwritten at any time
29136     setZIndex : function(index){
29137         if(this.modal){
29138             this.mask.setStyle("z-index", index);
29139         }
29140         if(this.shim){
29141             this.shim.setStyle("z-index", ++index);
29142         }
29143         if(this.shadow){
29144             this.shadow.setZIndex(++index);
29145         }
29146         this.el.setStyle("z-index", ++index);
29147         if(this.proxy){
29148             this.proxy.setStyle("z-index", ++index);
29149         }
29150         if(this.resizer){
29151             this.resizer.proxy.setStyle("z-index", ++index);
29152         }
29153
29154         this.lastZIndex = index;
29155     },
29156
29157     /**
29158      * Returns the element for this dialog
29159      * @return {Roo.Element} The underlying dialog Element
29160      */
29161     getEl : function(){
29162         return this.el;
29163     }
29164 });
29165
29166 /**
29167  * @class Roo.DialogManager
29168  * Provides global access to BasicDialogs that have been created and
29169  * support for z-indexing (layering) multiple open dialogs.
29170  */
29171 Roo.DialogManager = function(){
29172     var list = {};
29173     var accessList = [];
29174     var front = null;
29175
29176     // private
29177     var sortDialogs = function(d1, d2){
29178         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29179     };
29180
29181     // private
29182     var orderDialogs = function(){
29183         accessList.sort(sortDialogs);
29184         var seed = Roo.DialogManager.zseed;
29185         for(var i = 0, len = accessList.length; i < len; i++){
29186             var dlg = accessList[i];
29187             if(dlg){
29188                 dlg.setZIndex(seed + (i*10));
29189             }
29190         }
29191     };
29192
29193     return {
29194         /**
29195          * The starting z-index for BasicDialogs (defaults to 9000)
29196          * @type Number The z-index value
29197          */
29198         zseed : 9000,
29199
29200         // private
29201         register : function(dlg){
29202             list[dlg.id] = dlg;
29203             accessList.push(dlg);
29204         },
29205
29206         // private
29207         unregister : function(dlg){
29208             delete list[dlg.id];
29209             var i=0;
29210             var len=0;
29211             if(!accessList.indexOf){
29212                 for(  i = 0, len = accessList.length; i < len; i++){
29213                     if(accessList[i] == dlg){
29214                         accessList.splice(i, 1);
29215                         return;
29216                     }
29217                 }
29218             }else{
29219                  i = accessList.indexOf(dlg);
29220                 if(i != -1){
29221                     accessList.splice(i, 1);
29222                 }
29223             }
29224         },
29225
29226         /**
29227          * Gets a registered dialog by id
29228          * @param {String/Object} id The id of the dialog or a dialog
29229          * @return {Roo.BasicDialog} this
29230          */
29231         get : function(id){
29232             return typeof id == "object" ? id : list[id];
29233         },
29234
29235         /**
29236          * Brings the specified dialog to the front
29237          * @param {String/Object} dlg The id of the dialog or a dialog
29238          * @return {Roo.BasicDialog} this
29239          */
29240         bringToFront : function(dlg){
29241             dlg = this.get(dlg);
29242             if(dlg != front){
29243                 front = dlg;
29244                 dlg._lastAccess = new Date().getTime();
29245                 orderDialogs();
29246             }
29247             return dlg;
29248         },
29249
29250         /**
29251          * Sends the specified dialog to the back
29252          * @param {String/Object} dlg The id of the dialog or a dialog
29253          * @return {Roo.BasicDialog} this
29254          */
29255         sendToBack : function(dlg){
29256             dlg = this.get(dlg);
29257             dlg._lastAccess = -(new Date().getTime());
29258             orderDialogs();
29259             return dlg;
29260         },
29261
29262         /**
29263          * Hides all dialogs
29264          */
29265         hideAll : function(){
29266             for(var id in list){
29267                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29268                     list[id].hide();
29269                 }
29270             }
29271         }
29272     };
29273 }();
29274
29275 /**
29276  * @class Roo.LayoutDialog
29277  * @extends Roo.BasicDialog
29278  * Dialog which provides adjustments for working with a layout in a Dialog.
29279  * Add your necessary layout config options to the dialog's config.<br>
29280  * Example usage (including a nested layout):
29281  * <pre><code>
29282 if(!dialog){
29283     dialog = new Roo.LayoutDialog("download-dlg", {
29284         modal: true,
29285         width:600,
29286         height:450,
29287         shadow:true,
29288         minWidth:500,
29289         minHeight:350,
29290         autoTabs:true,
29291         proxyDrag:true,
29292         // layout config merges with the dialog config
29293         center:{
29294             tabPosition: "top",
29295             alwaysShowTabs: true
29296         }
29297     });
29298     dialog.addKeyListener(27, dialog.hide, dialog);
29299     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29300     dialog.addButton("Build It!", this.getDownload, this);
29301
29302     // we can even add nested layouts
29303     var innerLayout = new Roo.BorderLayout("dl-inner", {
29304         east: {
29305             initialSize: 200,
29306             autoScroll:true,
29307             split:true
29308         },
29309         center: {
29310             autoScroll:true
29311         }
29312     });
29313     innerLayout.beginUpdate();
29314     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29315     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29316     innerLayout.endUpdate(true);
29317
29318     var layout = dialog.getLayout();
29319     layout.beginUpdate();
29320     layout.add("center", new Roo.ContentPanel("standard-panel",
29321                         {title: "Download the Source", fitToFrame:true}));
29322     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29323                {title: "Build your own roo.js"}));
29324     layout.getRegion("center").showPanel(sp);
29325     layout.endUpdate();
29326 }
29327 </code></pre>
29328     * @constructor
29329     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29330     * @param {Object} config configuration options
29331   */
29332 Roo.LayoutDialog = function(el, cfg){
29333     
29334     var config=  cfg;
29335     if (typeof(cfg) == 'undefined') {
29336         config = Roo.apply({}, el);
29337         el = Roo.get( document.documentElement || document.body).createChild();
29338         //config.autoCreate = true;
29339     }
29340     
29341     
29342     config.autoTabs = false;
29343     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29344     this.body.setStyle({overflow:"hidden", position:"relative"});
29345     this.layout = new Roo.BorderLayout(this.body.dom, config);
29346     this.layout.monitorWindowResize = false;
29347     this.el.addClass("x-dlg-auto-layout");
29348     // fix case when center region overwrites center function
29349     this.center = Roo.BasicDialog.prototype.center;
29350     this.on("show", this.layout.layout, this.layout, true);
29351     if (config.items) {
29352         var xitems = config.items;
29353         delete config.items;
29354         Roo.each(xitems, this.addxtype, this);
29355     }
29356     
29357     
29358 };
29359 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29360     /**
29361      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29362      * @deprecated
29363      */
29364     endUpdate : function(){
29365         this.layout.endUpdate();
29366     },
29367
29368     /**
29369      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29370      *  @deprecated
29371      */
29372     beginUpdate : function(){
29373         this.layout.beginUpdate();
29374     },
29375
29376     /**
29377      * Get the BorderLayout for this dialog
29378      * @return {Roo.BorderLayout}
29379      */
29380     getLayout : function(){
29381         return this.layout;
29382     },
29383
29384     showEl : function(){
29385         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29386         if(Roo.isIE7){
29387             this.layout.layout();
29388         }
29389     },
29390
29391     // private
29392     // Use the syncHeightBeforeShow config option to control this automatically
29393     syncBodyHeight : function(){
29394         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29395         if(this.layout){this.layout.layout();}
29396     },
29397     
29398       /**
29399      * Add an xtype element (actually adds to the layout.)
29400      * @return {Object} xdata xtype object data.
29401      */
29402     
29403     addxtype : function(c) {
29404         return this.layout.addxtype(c);
29405     }
29406 });/*
29407  * Based on:
29408  * Ext JS Library 1.1.1
29409  * Copyright(c) 2006-2007, Ext JS, LLC.
29410  *
29411  * Originally Released Under LGPL - original licence link has changed is not relivant.
29412  *
29413  * Fork - LGPL
29414  * <script type="text/javascript">
29415  */
29416  
29417 /**
29418  * @class Roo.MessageBox
29419  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29420  * Example usage:
29421  *<pre><code>
29422 // Basic alert:
29423 Roo.Msg.alert('Status', 'Changes saved successfully.');
29424
29425 // Prompt for user data:
29426 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29427     if (btn == 'ok'){
29428         // process text value...
29429     }
29430 });
29431
29432 // Show a dialog using config options:
29433 Roo.Msg.show({
29434    title:'Save Changes?',
29435    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29436    buttons: Roo.Msg.YESNOCANCEL,
29437    fn: processResult,
29438    animEl: 'elId'
29439 });
29440 </code></pre>
29441  * @singleton
29442  */
29443 Roo.MessageBox = function(){
29444     var dlg, opt, mask, waitTimer;
29445     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29446     var buttons, activeTextEl, bwidth;
29447
29448     // private
29449     var handleButton = function(button){
29450         dlg.hide();
29451         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29452     };
29453
29454     // private
29455     var handleHide = function(){
29456         if(opt && opt.cls){
29457             dlg.el.removeClass(opt.cls);
29458         }
29459         if(waitTimer){
29460             Roo.TaskMgr.stop(waitTimer);
29461             waitTimer = null;
29462         }
29463     };
29464
29465     // private
29466     var updateButtons = function(b){
29467         var width = 0;
29468         if(!b){
29469             buttons["ok"].hide();
29470             buttons["cancel"].hide();
29471             buttons["yes"].hide();
29472             buttons["no"].hide();
29473             dlg.footer.dom.style.display = 'none';
29474             return width;
29475         }
29476         dlg.footer.dom.style.display = '';
29477         for(var k in buttons){
29478             if(typeof buttons[k] != "function"){
29479                 if(b[k]){
29480                     buttons[k].show();
29481                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29482                     width += buttons[k].el.getWidth()+15;
29483                 }else{
29484                     buttons[k].hide();
29485                 }
29486             }
29487         }
29488         return width;
29489     };
29490
29491     // private
29492     var handleEsc = function(d, k, e){
29493         if(opt && opt.closable !== false){
29494             dlg.hide();
29495         }
29496         if(e){
29497             e.stopEvent();
29498         }
29499     };
29500
29501     return {
29502         /**
29503          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29504          * @return {Roo.BasicDialog} The BasicDialog element
29505          */
29506         getDialog : function(){
29507            if(!dlg){
29508                 dlg = new Roo.BasicDialog("x-msg-box", {
29509                     autoCreate : true,
29510                     shadow: true,
29511                     draggable: true,
29512                     resizable:false,
29513                     constraintoviewport:false,
29514                     fixedcenter:true,
29515                     collapsible : false,
29516                     shim:true,
29517                     modal: true,
29518                     width:400, height:100,
29519                     buttonAlign:"center",
29520                     closeClick : function(){
29521                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29522                             handleButton("no");
29523                         }else{
29524                             handleButton("cancel");
29525                         }
29526                     }
29527                 });
29528                 dlg.on("hide", handleHide);
29529                 mask = dlg.mask;
29530                 dlg.addKeyListener(27, handleEsc);
29531                 buttons = {};
29532                 var bt = this.buttonText;
29533                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29534                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29535                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29536                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29537                 bodyEl = dlg.body.createChild({
29538
29539                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
29540                 });
29541                 msgEl = bodyEl.dom.firstChild;
29542                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29543                 textboxEl.enableDisplayMode();
29544                 textboxEl.addKeyListener([10,13], function(){
29545                     if(dlg.isVisible() && opt && opt.buttons){
29546                         if(opt.buttons.ok){
29547                             handleButton("ok");
29548                         }else if(opt.buttons.yes){
29549                             handleButton("yes");
29550                         }
29551                     }
29552                 });
29553                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29554                 textareaEl.enableDisplayMode();
29555                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29556                 progressEl.enableDisplayMode();
29557                 var pf = progressEl.dom.firstChild;
29558                 if (pf) {
29559                     pp = Roo.get(pf.firstChild);
29560                     pp.setHeight(pf.offsetHeight);
29561                 }
29562                 
29563             }
29564             return dlg;
29565         },
29566
29567         /**
29568          * Updates the message box body text
29569          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29570          * the XHTML-compliant non-breaking space character '&amp;#160;')
29571          * @return {Roo.MessageBox} This message box
29572          */
29573         updateText : function(text){
29574             if(!dlg.isVisible() && !opt.width){
29575                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29576             }
29577             msgEl.innerHTML = text || '&#160;';
29578             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29579                         Math.max(opt.minWidth || this.minWidth, bwidth));
29580             if(opt.prompt){
29581                 activeTextEl.setWidth(w);
29582             }
29583             if(dlg.isVisible()){
29584                 dlg.fixedcenter = false;
29585             }
29586             dlg.setContentSize(w, bodyEl.getHeight());
29587             if(dlg.isVisible()){
29588                 dlg.fixedcenter = true;
29589             }
29590             return this;
29591         },
29592
29593         /**
29594          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29595          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29596          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29597          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29598          * @return {Roo.MessageBox} This message box
29599          */
29600         updateProgress : function(value, text){
29601             if(text){
29602                 this.updateText(text);
29603             }
29604             if (pp) { // weird bug on my firefox - for some reason this is not defined
29605                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29606             }
29607             return this;
29608         },        
29609
29610         /**
29611          * Returns true if the message box is currently displayed
29612          * @return {Boolean} True if the message box is visible, else false
29613          */
29614         isVisible : function(){
29615             return dlg && dlg.isVisible();  
29616         },
29617
29618         /**
29619          * Hides the message box if it is displayed
29620          */
29621         hide : function(){
29622             if(this.isVisible()){
29623                 dlg.hide();
29624             }  
29625         },
29626
29627         /**
29628          * Displays a new message box, or reinitializes an existing message box, based on the config options
29629          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29630          * The following config object properties are supported:
29631          * <pre>
29632 Property    Type             Description
29633 ----------  ---------------  ------------------------------------------------------------------------------------
29634 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29635                                    closes (defaults to undefined)
29636 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29637                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29638 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29639                                    progress and wait dialogs will ignore this property and always hide the
29640                                    close button as they can only be closed programmatically.
29641 cls               String           A custom CSS class to apply to the message box element
29642 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29643                                    displayed (defaults to 75)
29644 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29645                                    function will be btn (the name of the button that was clicked, if applicable,
29646                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29647                                    Progress and wait dialogs will ignore this option since they do not respond to
29648                                    user actions and can only be closed programmatically, so any required function
29649                                    should be called by the same code after it closes the dialog.
29650 icon              String           A CSS class that provides a background image to be used as an icon for
29651                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29652 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29653 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29654 modal             Boolean          False to allow user interaction with the page while the message box is
29655                                    displayed (defaults to true)
29656 msg               String           A string that will replace the existing message box body text (defaults
29657                                    to the XHTML-compliant non-breaking space character '&#160;')
29658 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29659 progress          Boolean          True to display a progress bar (defaults to false)
29660 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29661 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29662 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29663 title             String           The title text
29664 value             String           The string value to set into the active textbox element if displayed
29665 wait              Boolean          True to display a progress bar (defaults to false)
29666 width             Number           The width of the dialog in pixels
29667 </pre>
29668          *
29669          * Example usage:
29670          * <pre><code>
29671 Roo.Msg.show({
29672    title: 'Address',
29673    msg: 'Please enter your address:',
29674    width: 300,
29675    buttons: Roo.MessageBox.OKCANCEL,
29676    multiline: true,
29677    fn: saveAddress,
29678    animEl: 'addAddressBtn'
29679 });
29680 </code></pre>
29681          * @param {Object} config Configuration options
29682          * @return {Roo.MessageBox} This message box
29683          */
29684         show : function(options){
29685             if(this.isVisible()){
29686                 this.hide();
29687             }
29688             var d = this.getDialog();
29689             opt = options;
29690             d.setTitle(opt.title || "&#160;");
29691             d.close.setDisplayed(opt.closable !== false);
29692             activeTextEl = textboxEl;
29693             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29694             if(opt.prompt){
29695                 if(opt.multiline){
29696                     textboxEl.hide();
29697                     textareaEl.show();
29698                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29699                         opt.multiline : this.defaultTextHeight);
29700                     activeTextEl = textareaEl;
29701                 }else{
29702                     textboxEl.show();
29703                     textareaEl.hide();
29704                 }
29705             }else{
29706                 textboxEl.hide();
29707                 textareaEl.hide();
29708             }
29709             progressEl.setDisplayed(opt.progress === true);
29710             this.updateProgress(0);
29711             activeTextEl.dom.value = opt.value || "";
29712             if(opt.prompt){
29713                 dlg.setDefaultButton(activeTextEl);
29714             }else{
29715                 var bs = opt.buttons;
29716                 var db = null;
29717                 if(bs && bs.ok){
29718                     db = buttons["ok"];
29719                 }else if(bs && bs.yes){
29720                     db = buttons["yes"];
29721                 }
29722                 dlg.setDefaultButton(db);
29723             }
29724             bwidth = updateButtons(opt.buttons);
29725             this.updateText(opt.msg);
29726             if(opt.cls){
29727                 d.el.addClass(opt.cls);
29728             }
29729             d.proxyDrag = opt.proxyDrag === true;
29730             d.modal = opt.modal !== false;
29731             d.mask = opt.modal !== false ? mask : false;
29732             if(!d.isVisible()){
29733                 // force it to the end of the z-index stack so it gets a cursor in FF
29734                 document.body.appendChild(dlg.el.dom);
29735                 d.animateTarget = null;
29736                 d.show(options.animEl);
29737             }
29738             return this;
29739         },
29740
29741         /**
29742          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29743          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29744          * and closing the message box when the process is complete.
29745          * @param {String} title The title bar text
29746          * @param {String} msg The message box body text
29747          * @return {Roo.MessageBox} This message box
29748          */
29749         progress : function(title, msg){
29750             this.show({
29751                 title : title,
29752                 msg : msg,
29753                 buttons: false,
29754                 progress:true,
29755                 closable:false,
29756                 minWidth: this.minProgressWidth,
29757                 modal : true
29758             });
29759             return this;
29760         },
29761
29762         /**
29763          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29764          * If a callback function is passed it will be called after the user clicks the button, and the
29765          * id of the button that was clicked will be passed as the only parameter to the callback
29766          * (could also be the top-right close button).
29767          * @param {String} title The title bar text
29768          * @param {String} msg The message box body text
29769          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29770          * @param {Object} scope (optional) The scope of the callback function
29771          * @return {Roo.MessageBox} This message box
29772          */
29773         alert : function(title, msg, fn, scope){
29774             this.show({
29775                 title : title,
29776                 msg : msg,
29777                 buttons: this.OK,
29778                 fn: fn,
29779                 scope : scope,
29780                 modal : true
29781             });
29782             return this;
29783         },
29784
29785         /**
29786          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29787          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29788          * You are responsible for closing the message box when the process is complete.
29789          * @param {String} msg The message box body text
29790          * @param {String} title (optional) The title bar text
29791          * @return {Roo.MessageBox} This message box
29792          */
29793         wait : function(msg, title){
29794             this.show({
29795                 title : title,
29796                 msg : msg,
29797                 buttons: false,
29798                 closable:false,
29799                 progress:true,
29800                 modal:true,
29801                 width:300,
29802                 wait:true
29803             });
29804             waitTimer = Roo.TaskMgr.start({
29805                 run: function(i){
29806                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29807                 },
29808                 interval: 1000
29809             });
29810             return this;
29811         },
29812
29813         /**
29814          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29815          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29816          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29817          * @param {String} title The title bar text
29818          * @param {String} msg The message box body text
29819          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29820          * @param {Object} scope (optional) The scope of the callback function
29821          * @return {Roo.MessageBox} This message box
29822          */
29823         confirm : function(title, msg, fn, scope){
29824             this.show({
29825                 title : title,
29826                 msg : msg,
29827                 buttons: this.YESNO,
29828                 fn: fn,
29829                 scope : scope,
29830                 modal : true
29831             });
29832             return this;
29833         },
29834
29835         /**
29836          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29837          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29838          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29839          * (could also be the top-right close button) and the text that was entered will be passed as the two
29840          * parameters to the callback.
29841          * @param {String} title The title bar text
29842          * @param {String} msg The message box body text
29843          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29844          * @param {Object} scope (optional) The scope of the callback function
29845          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29846          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29847          * @return {Roo.MessageBox} This message box
29848          */
29849         prompt : function(title, msg, fn, scope, multiline){
29850             this.show({
29851                 title : title,
29852                 msg : msg,
29853                 buttons: this.OKCANCEL,
29854                 fn: fn,
29855                 minWidth:250,
29856                 scope : scope,
29857                 prompt:true,
29858                 multiline: multiline,
29859                 modal : true
29860             });
29861             return this;
29862         },
29863
29864         /**
29865          * Button config that displays a single OK button
29866          * @type Object
29867          */
29868         OK : {ok:true},
29869         /**
29870          * Button config that displays Yes and No buttons
29871          * @type Object
29872          */
29873         YESNO : {yes:true, no:true},
29874         /**
29875          * Button config that displays OK and Cancel buttons
29876          * @type Object
29877          */
29878         OKCANCEL : {ok:true, cancel:true},
29879         /**
29880          * Button config that displays Yes, No and Cancel buttons
29881          * @type Object
29882          */
29883         YESNOCANCEL : {yes:true, no:true, cancel:true},
29884
29885         /**
29886          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29887          * @type Number
29888          */
29889         defaultTextHeight : 75,
29890         /**
29891          * The maximum width in pixels of the message box (defaults to 600)
29892          * @type Number
29893          */
29894         maxWidth : 600,
29895         /**
29896          * The minimum width in pixels of the message box (defaults to 100)
29897          * @type Number
29898          */
29899         minWidth : 100,
29900         /**
29901          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29902          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29903          * @type Number
29904          */
29905         minProgressWidth : 250,
29906         /**
29907          * An object containing the default button text strings that can be overriden for localized language support.
29908          * Supported properties are: ok, cancel, yes and no.
29909          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29910          * @type Object
29911          */
29912         buttonText : {
29913             ok : "OK",
29914             cancel : "Cancel",
29915             yes : "Yes",
29916             no : "No"
29917         }
29918     };
29919 }();
29920
29921 /**
29922  * Shorthand for {@link Roo.MessageBox}
29923  */
29924 Roo.Msg = Roo.MessageBox;/*
29925  * Based on:
29926  * Ext JS Library 1.1.1
29927  * Copyright(c) 2006-2007, Ext JS, LLC.
29928  *
29929  * Originally Released Under LGPL - original licence link has changed is not relivant.
29930  *
29931  * Fork - LGPL
29932  * <script type="text/javascript">
29933  */
29934 /**
29935  * @class Roo.QuickTips
29936  * Provides attractive and customizable tooltips for any element.
29937  * @singleton
29938  */
29939 Roo.QuickTips = function(){
29940     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
29941     var ce, bd, xy, dd;
29942     var visible = false, disabled = true, inited = false;
29943     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
29944     
29945     var onOver = function(e){
29946         if(disabled){
29947             return;
29948         }
29949         var t = e.getTarget();
29950         if(!t || t.nodeType !== 1 || t == document || t == document.body){
29951             return;
29952         }
29953         if(ce && t == ce.el){
29954             clearTimeout(hideProc);
29955             return;
29956         }
29957         if(t && tagEls[t.id]){
29958             tagEls[t.id].el = t;
29959             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
29960             return;
29961         }
29962         var ttp, et = Roo.fly(t);
29963         var ns = cfg.namespace;
29964         if(tm.interceptTitles && t.title){
29965             ttp = t.title;
29966             t.qtip = ttp;
29967             t.removeAttribute("title");
29968             e.preventDefault();
29969         }else{
29970             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
29971         }
29972         if(ttp){
29973             showProc = show.defer(tm.showDelay, tm, [{
29974                 el: t, 
29975                 text: ttp, 
29976                 width: et.getAttributeNS(ns, cfg.width),
29977                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
29978                 title: et.getAttributeNS(ns, cfg.title),
29979                     cls: et.getAttributeNS(ns, cfg.cls)
29980             }]);
29981         }
29982     };
29983     
29984     var onOut = function(e){
29985         clearTimeout(showProc);
29986         var t = e.getTarget();
29987         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
29988             hideProc = setTimeout(hide, tm.hideDelay);
29989         }
29990     };
29991     
29992     var onMove = function(e){
29993         if(disabled){
29994             return;
29995         }
29996         xy = e.getXY();
29997         xy[1] += 18;
29998         if(tm.trackMouse && ce){
29999             el.setXY(xy);
30000         }
30001     };
30002     
30003     var onDown = function(e){
30004         clearTimeout(showProc);
30005         clearTimeout(hideProc);
30006         if(!e.within(el)){
30007             if(tm.hideOnClick){
30008                 hide();
30009                 tm.disable();
30010                 tm.enable.defer(100, tm);
30011             }
30012         }
30013     };
30014     
30015     var getPad = function(){
30016         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30017     };
30018
30019     var show = function(o){
30020         if(disabled){
30021             return;
30022         }
30023         clearTimeout(dismissProc);
30024         ce = o;
30025         if(removeCls){ // in case manually hidden
30026             el.removeClass(removeCls);
30027             removeCls = null;
30028         }
30029         if(ce.cls){
30030             el.addClass(ce.cls);
30031             removeCls = ce.cls;
30032         }
30033         if(ce.title){
30034             tipTitle.update(ce.title);
30035             tipTitle.show();
30036         }else{
30037             tipTitle.update('');
30038             tipTitle.hide();
30039         }
30040         el.dom.style.width  = tm.maxWidth+'px';
30041         //tipBody.dom.style.width = '';
30042         tipBodyText.update(o.text);
30043         var p = getPad(), w = ce.width;
30044         if(!w){
30045             var td = tipBodyText.dom;
30046             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30047             if(aw > tm.maxWidth){
30048                 w = tm.maxWidth;
30049             }else if(aw < tm.minWidth){
30050                 w = tm.minWidth;
30051             }else{
30052                 w = aw;
30053             }
30054         }
30055         //tipBody.setWidth(w);
30056         el.setWidth(parseInt(w, 10) + p);
30057         if(ce.autoHide === false){
30058             close.setDisplayed(true);
30059             if(dd){
30060                 dd.unlock();
30061             }
30062         }else{
30063             close.setDisplayed(false);
30064             if(dd){
30065                 dd.lock();
30066             }
30067         }
30068         if(xy){
30069             el.avoidY = xy[1]-18;
30070             el.setXY(xy);
30071         }
30072         if(tm.animate){
30073             el.setOpacity(.1);
30074             el.setStyle("visibility", "visible");
30075             el.fadeIn({callback: afterShow});
30076         }else{
30077             afterShow();
30078         }
30079     };
30080     
30081     var afterShow = function(){
30082         if(ce){
30083             el.show();
30084             esc.enable();
30085             if(tm.autoDismiss && ce.autoHide !== false){
30086                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30087             }
30088         }
30089     };
30090     
30091     var hide = function(noanim){
30092         clearTimeout(dismissProc);
30093         clearTimeout(hideProc);
30094         ce = null;
30095         if(el.isVisible()){
30096             esc.disable();
30097             if(noanim !== true && tm.animate){
30098                 el.fadeOut({callback: afterHide});
30099             }else{
30100                 afterHide();
30101             } 
30102         }
30103     };
30104     
30105     var afterHide = function(){
30106         el.hide();
30107         if(removeCls){
30108             el.removeClass(removeCls);
30109             removeCls = null;
30110         }
30111     };
30112     
30113     return {
30114         /**
30115         * @cfg {Number} minWidth
30116         * The minimum width of the quick tip (defaults to 40)
30117         */
30118        minWidth : 40,
30119         /**
30120         * @cfg {Number} maxWidth
30121         * The maximum width of the quick tip (defaults to 300)
30122         */
30123        maxWidth : 300,
30124         /**
30125         * @cfg {Boolean} interceptTitles
30126         * True to automatically use the element's DOM title value if available (defaults to false)
30127         */
30128        interceptTitles : false,
30129         /**
30130         * @cfg {Boolean} trackMouse
30131         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30132         */
30133        trackMouse : false,
30134         /**
30135         * @cfg {Boolean} hideOnClick
30136         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30137         */
30138        hideOnClick : true,
30139         /**
30140         * @cfg {Number} showDelay
30141         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30142         */
30143        showDelay : 500,
30144         /**
30145         * @cfg {Number} hideDelay
30146         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30147         */
30148        hideDelay : 200,
30149         /**
30150         * @cfg {Boolean} autoHide
30151         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30152         * Used in conjunction with hideDelay.
30153         */
30154        autoHide : true,
30155         /**
30156         * @cfg {Boolean}
30157         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30158         * (defaults to true).  Used in conjunction with autoDismissDelay.
30159         */
30160        autoDismiss : true,
30161         /**
30162         * @cfg {Number}
30163         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30164         */
30165        autoDismissDelay : 5000,
30166        /**
30167         * @cfg {Boolean} animate
30168         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30169         */
30170        animate : false,
30171
30172        /**
30173         * @cfg {String} title
30174         * Title text to display (defaults to '').  This can be any valid HTML markup.
30175         */
30176         title: '',
30177        /**
30178         * @cfg {String} text
30179         * Body text to display (defaults to '').  This can be any valid HTML markup.
30180         */
30181         text : '',
30182        /**
30183         * @cfg {String} cls
30184         * A CSS class to apply to the base quick tip element (defaults to '').
30185         */
30186         cls : '',
30187        /**
30188         * @cfg {Number} width
30189         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30190         * minWidth or maxWidth.
30191         */
30192         width : null,
30193
30194     /**
30195      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30196      * or display QuickTips in a page.
30197      */
30198        init : function(){
30199           tm = Roo.QuickTips;
30200           cfg = tm.tagConfig;
30201           if(!inited){
30202               if(!Roo.isReady){ // allow calling of init() before onReady
30203                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30204                   return;
30205               }
30206               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30207               el.fxDefaults = {stopFx: true};
30208               // maximum custom styling
30209               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30210               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30211               tipTitle = el.child('h3');
30212               tipTitle.enableDisplayMode("block");
30213               tipBody = el.child('div.x-tip-bd');
30214               tipBodyText = el.child('div.x-tip-bd-inner');
30215               //bdLeft = el.child('div.x-tip-bd-left');
30216               //bdRight = el.child('div.x-tip-bd-right');
30217               close = el.child('div.x-tip-close');
30218               close.enableDisplayMode("block");
30219               close.on("click", hide);
30220               var d = Roo.get(document);
30221               d.on("mousedown", onDown);
30222               d.on("mouseover", onOver);
30223               d.on("mouseout", onOut);
30224               d.on("mousemove", onMove);
30225               esc = d.addKeyListener(27, hide);
30226               esc.disable();
30227               if(Roo.dd.DD){
30228                   dd = el.initDD("default", null, {
30229                       onDrag : function(){
30230                           el.sync();  
30231                       }
30232                   });
30233                   dd.setHandleElId(tipTitle.id);
30234                   dd.lock();
30235               }
30236               inited = true;
30237           }
30238           this.enable(); 
30239        },
30240
30241     /**
30242      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30243      * are supported:
30244      * <pre>
30245 Property    Type                   Description
30246 ----------  ---------------------  ------------------------------------------------------------------------
30247 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30248      * </ul>
30249      * @param {Object} config The config object
30250      */
30251        register : function(config){
30252            var cs = config instanceof Array ? config : arguments;
30253            for(var i = 0, len = cs.length; i < len; i++) {
30254                var c = cs[i];
30255                var target = c.target;
30256                if(target){
30257                    if(target instanceof Array){
30258                        for(var j = 0, jlen = target.length; j < jlen; j++){
30259                            tagEls[target[j]] = c;
30260                        }
30261                    }else{
30262                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30263                    }
30264                }
30265            }
30266        },
30267
30268     /**
30269      * Removes this quick tip from its element and destroys it.
30270      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30271      */
30272        unregister : function(el){
30273            delete tagEls[Roo.id(el)];
30274        },
30275
30276     /**
30277      * Enable this quick tip.
30278      */
30279        enable : function(){
30280            if(inited && disabled){
30281                locks.pop();
30282                if(locks.length < 1){
30283                    disabled = false;
30284                }
30285            }
30286        },
30287
30288     /**
30289      * Disable this quick tip.
30290      */
30291        disable : function(){
30292           disabled = true;
30293           clearTimeout(showProc);
30294           clearTimeout(hideProc);
30295           clearTimeout(dismissProc);
30296           if(ce){
30297               hide(true);
30298           }
30299           locks.push(1);
30300        },
30301
30302     /**
30303      * Returns true if the quick tip is enabled, else false.
30304      */
30305        isEnabled : function(){
30306             return !disabled;
30307        },
30308
30309         // private
30310        tagConfig : {
30311            namespace : "ext",
30312            attribute : "qtip",
30313            width : "width",
30314            target : "target",
30315            title : "qtitle",
30316            hide : "hide",
30317            cls : "qclass"
30318        }
30319    };
30320 }();
30321
30322 // backwards compat
30323 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30324  * Based on:
30325  * Ext JS Library 1.1.1
30326  * Copyright(c) 2006-2007, Ext JS, LLC.
30327  *
30328  * Originally Released Under LGPL - original licence link has changed is not relivant.
30329  *
30330  * Fork - LGPL
30331  * <script type="text/javascript">
30332  */
30333  
30334
30335 /**
30336  * @class Roo.tree.TreePanel
30337  * @extends Roo.data.Tree
30338
30339  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30340  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30341  * @cfg {Boolean} enableDD true to enable drag and drop
30342  * @cfg {Boolean} enableDrag true to enable just drag
30343  * @cfg {Boolean} enableDrop true to enable just drop
30344  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30345  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30346  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30347  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30348  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30349  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30350  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30351  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30352  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30353  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30354  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30355  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30356  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30357  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30358  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30359  * 
30360  * @constructor
30361  * @param {String/HTMLElement/Element} el The container element
30362  * @param {Object} config
30363  */
30364 Roo.tree.TreePanel = function(el, config){
30365     var root = false;
30366     var loader = false;
30367     if (config.root) {
30368         root = config.root;
30369         delete config.root;
30370     }
30371     if (config.loader) {
30372         loader = config.loader;
30373         delete config.loader;
30374     }
30375     
30376     Roo.apply(this, config);
30377     Roo.tree.TreePanel.superclass.constructor.call(this);
30378     this.el = Roo.get(el);
30379     this.el.addClass('x-tree');
30380     //console.log(root);
30381     if (root) {
30382         this.setRootNode( Roo.factory(root, Roo.tree));
30383     }
30384     if (loader) {
30385         this.loader = Roo.factory(loader, Roo.tree);
30386     }
30387    /**
30388     * Read-only. The id of the container element becomes this TreePanel's id.
30389     */
30390    this.id = this.el.id;
30391    this.addEvents({
30392         /**
30393         * @event beforeload
30394         * Fires before a node is loaded, return false to cancel
30395         * @param {Node} node The node being loaded
30396         */
30397         "beforeload" : true,
30398         /**
30399         * @event load
30400         * Fires when a node is loaded
30401         * @param {Node} node The node that was loaded
30402         */
30403         "load" : true,
30404         /**
30405         * @event textchange
30406         * Fires when the text for a node is changed
30407         * @param {Node} node The node
30408         * @param {String} text The new text
30409         * @param {String} oldText The old text
30410         */
30411         "textchange" : true,
30412         /**
30413         * @event beforeexpand
30414         * Fires before a node is expanded, return false to cancel.
30415         * @param {Node} node The node
30416         * @param {Boolean} deep
30417         * @param {Boolean} anim
30418         */
30419         "beforeexpand" : true,
30420         /**
30421         * @event beforecollapse
30422         * Fires before a node is collapsed, return false to cancel.
30423         * @param {Node} node The node
30424         * @param {Boolean} deep
30425         * @param {Boolean} anim
30426         */
30427         "beforecollapse" : true,
30428         /**
30429         * @event expand
30430         * Fires when a node is expanded
30431         * @param {Node} node The node
30432         */
30433         "expand" : true,
30434         /**
30435         * @event disabledchange
30436         * Fires when the disabled status of a node changes
30437         * @param {Node} node The node
30438         * @param {Boolean} disabled
30439         */
30440         "disabledchange" : true,
30441         /**
30442         * @event collapse
30443         * Fires when a node is collapsed
30444         * @param {Node} node The node
30445         */
30446         "collapse" : true,
30447         /**
30448         * @event beforeclick
30449         * Fires before click processing on a node. Return false to cancel the default action.
30450         * @param {Node} node The node
30451         * @param {Roo.EventObject} e The event object
30452         */
30453         "beforeclick":true,
30454         /**
30455         * @event checkchange
30456         * Fires when a node with a checkbox's checked property changes
30457         * @param {Node} this This node
30458         * @param {Boolean} checked
30459         */
30460         "checkchange":true,
30461         /**
30462         * @event click
30463         * Fires when a node is clicked
30464         * @param {Node} node The node
30465         * @param {Roo.EventObject} e The event object
30466         */
30467         "click":true,
30468         /**
30469         * @event dblclick
30470         * Fires when a node is double clicked
30471         * @param {Node} node The node
30472         * @param {Roo.EventObject} e The event object
30473         */
30474         "dblclick":true,
30475         /**
30476         * @event contextmenu
30477         * Fires when a node is right clicked
30478         * @param {Node} node The node
30479         * @param {Roo.EventObject} e The event object
30480         */
30481         "contextmenu":true,
30482         /**
30483         * @event beforechildrenrendered
30484         * Fires right before the child nodes for a node are rendered
30485         * @param {Node} node The node
30486         */
30487         "beforechildrenrendered":true,
30488        /**
30489              * @event startdrag
30490              * Fires when a node starts being dragged
30491              * @param {Roo.tree.TreePanel} this
30492              * @param {Roo.tree.TreeNode} node
30493              * @param {event} e The raw browser event
30494              */ 
30495             "startdrag" : true,
30496             /**
30497              * @event enddrag
30498              * Fires when a drag operation is complete
30499              * @param {Roo.tree.TreePanel} this
30500              * @param {Roo.tree.TreeNode} node
30501              * @param {event} e The raw browser event
30502              */
30503             "enddrag" : true,
30504             /**
30505              * @event dragdrop
30506              * Fires when a dragged node is dropped on a valid DD target
30507              * @param {Roo.tree.TreePanel} this
30508              * @param {Roo.tree.TreeNode} node
30509              * @param {DD} dd The dd it was dropped on
30510              * @param {event} e The raw browser event
30511              */
30512             "dragdrop" : true,
30513             /**
30514              * @event beforenodedrop
30515              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30516              * passed to handlers has the following properties:<br />
30517              * <ul style="padding:5px;padding-left:16px;">
30518              * <li>tree - The TreePanel</li>
30519              * <li>target - The node being targeted for the drop</li>
30520              * <li>data - The drag data from the drag source</li>
30521              * <li>point - The point of the drop - append, above or below</li>
30522              * <li>source - The drag source</li>
30523              * <li>rawEvent - Raw mouse event</li>
30524              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30525              * to be inserted by setting them on this object.</li>
30526              * <li>cancel - Set this to true to cancel the drop.</li>
30527              * </ul>
30528              * @param {Object} dropEvent
30529              */
30530             "beforenodedrop" : true,
30531             /**
30532              * @event nodedrop
30533              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30534              * passed to handlers has the following properties:<br />
30535              * <ul style="padding:5px;padding-left:16px;">
30536              * <li>tree - The TreePanel</li>
30537              * <li>target - The node being targeted for the drop</li>
30538              * <li>data - The drag data from the drag source</li>
30539              * <li>point - The point of the drop - append, above or below</li>
30540              * <li>source - The drag source</li>
30541              * <li>rawEvent - Raw mouse event</li>
30542              * <li>dropNode - Dropped node(s).</li>
30543              * </ul>
30544              * @param {Object} dropEvent
30545              */
30546             "nodedrop" : true,
30547              /**
30548              * @event nodedragover
30549              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30550              * passed to handlers has the following properties:<br />
30551              * <ul style="padding:5px;padding-left:16px;">
30552              * <li>tree - The TreePanel</li>
30553              * <li>target - The node being targeted for the drop</li>
30554              * <li>data - The drag data from the drag source</li>
30555              * <li>point - The point of the drop - append, above or below</li>
30556              * <li>source - The drag source</li>
30557              * <li>rawEvent - Raw mouse event</li>
30558              * <li>dropNode - Drop node(s) provided by the source.</li>
30559              * <li>cancel - Set this to true to signal drop not allowed.</li>
30560              * </ul>
30561              * @param {Object} dragOverEvent
30562              */
30563             "nodedragover" : true
30564         
30565    });
30566    if(this.singleExpand){
30567        this.on("beforeexpand", this.restrictExpand, this);
30568    }
30569 };
30570 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30571     rootVisible : true,
30572     animate: Roo.enableFx,
30573     lines : true,
30574     enableDD : false,
30575     hlDrop : Roo.enableFx,
30576   
30577     renderer: false,
30578     
30579     rendererTip: false,
30580     // private
30581     restrictExpand : function(node){
30582         var p = node.parentNode;
30583         if(p){
30584             if(p.expandedChild && p.expandedChild.parentNode == p){
30585                 p.expandedChild.collapse();
30586             }
30587             p.expandedChild = node;
30588         }
30589     },
30590
30591     // private override
30592     setRootNode : function(node){
30593         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30594         if(!this.rootVisible){
30595             node.ui = new Roo.tree.RootTreeNodeUI(node);
30596         }
30597         return node;
30598     },
30599
30600     /**
30601      * Returns the container element for this TreePanel
30602      */
30603     getEl : function(){
30604         return this.el;
30605     },
30606
30607     /**
30608      * Returns the default TreeLoader for this TreePanel
30609      */
30610     getLoader : function(){
30611         return this.loader;
30612     },
30613
30614     /**
30615      * Expand all nodes
30616      */
30617     expandAll : function(){
30618         this.root.expand(true);
30619     },
30620
30621     /**
30622      * Collapse all nodes
30623      */
30624     collapseAll : function(){
30625         this.root.collapse(true);
30626     },
30627
30628     /**
30629      * Returns the selection model used by this TreePanel
30630      */
30631     getSelectionModel : function(){
30632         if(!this.selModel){
30633             this.selModel = new Roo.tree.DefaultSelectionModel();
30634         }
30635         return this.selModel;
30636     },
30637
30638     /**
30639      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30640      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30641      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30642      * @return {Array}
30643      */
30644     getChecked : function(a, startNode){
30645         startNode = startNode || this.root;
30646         var r = [];
30647         var f = function(){
30648             if(this.attributes.checked){
30649                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30650             }
30651         }
30652         startNode.cascade(f);
30653         return r;
30654     },
30655
30656     /**
30657      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30658      * @param {String} path
30659      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30660      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30661      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30662      */
30663     expandPath : function(path, attr, callback){
30664         attr = attr || "id";
30665         var keys = path.split(this.pathSeparator);
30666         var curNode = this.root;
30667         if(curNode.attributes[attr] != keys[1]){ // invalid root
30668             if(callback){
30669                 callback(false, null);
30670             }
30671             return;
30672         }
30673         var index = 1;
30674         var f = function(){
30675             if(++index == keys.length){
30676                 if(callback){
30677                     callback(true, curNode);
30678                 }
30679                 return;
30680             }
30681             var c = curNode.findChild(attr, keys[index]);
30682             if(!c){
30683                 if(callback){
30684                     callback(false, curNode);
30685                 }
30686                 return;
30687             }
30688             curNode = c;
30689             c.expand(false, false, f);
30690         };
30691         curNode.expand(false, false, f);
30692     },
30693
30694     /**
30695      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30696      * @param {String} path
30697      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30698      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30699      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30700      */
30701     selectPath : function(path, attr, callback){
30702         attr = attr || "id";
30703         var keys = path.split(this.pathSeparator);
30704         var v = keys.pop();
30705         if(keys.length > 0){
30706             var f = function(success, node){
30707                 if(success && node){
30708                     var n = node.findChild(attr, v);
30709                     if(n){
30710                         n.select();
30711                         if(callback){
30712                             callback(true, n);
30713                         }
30714                     }else if(callback){
30715                         callback(false, n);
30716                     }
30717                 }else{
30718                     if(callback){
30719                         callback(false, n);
30720                     }
30721                 }
30722             };
30723             this.expandPath(keys.join(this.pathSeparator), attr, f);
30724         }else{
30725             this.root.select();
30726             if(callback){
30727                 callback(true, this.root);
30728             }
30729         }
30730     },
30731
30732     getTreeEl : function(){
30733         return this.el;
30734     },
30735
30736     /**
30737      * Trigger rendering of this TreePanel
30738      */
30739     render : function(){
30740         if (this.innerCt) {
30741             return this; // stop it rendering more than once!!
30742         }
30743         
30744         this.innerCt = this.el.createChild({tag:"ul",
30745                cls:"x-tree-root-ct " +
30746                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30747
30748         if(this.containerScroll){
30749             Roo.dd.ScrollManager.register(this.el);
30750         }
30751         if((this.enableDD || this.enableDrop) && !this.dropZone){
30752            /**
30753             * The dropZone used by this tree if drop is enabled
30754             * @type Roo.tree.TreeDropZone
30755             */
30756              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30757                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30758            });
30759         }
30760         if((this.enableDD || this.enableDrag) && !this.dragZone){
30761            /**
30762             * The dragZone used by this tree if drag is enabled
30763             * @type Roo.tree.TreeDragZone
30764             */
30765             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30766                ddGroup: this.ddGroup || "TreeDD",
30767                scroll: this.ddScroll
30768            });
30769         }
30770         this.getSelectionModel().init(this);
30771         if (!this.root) {
30772             console.log("ROOT not set in tree");
30773             return;
30774         }
30775         this.root.render();
30776         if(!this.rootVisible){
30777             this.root.renderChildren();
30778         }
30779         return this;
30780     }
30781 });/*
30782  * Based on:
30783  * Ext JS Library 1.1.1
30784  * Copyright(c) 2006-2007, Ext JS, LLC.
30785  *
30786  * Originally Released Under LGPL - original licence link has changed is not relivant.
30787  *
30788  * Fork - LGPL
30789  * <script type="text/javascript">
30790  */
30791  
30792
30793 /**
30794  * @class Roo.tree.DefaultSelectionModel
30795  * @extends Roo.util.Observable
30796  * The default single selection for a TreePanel.
30797  */
30798 Roo.tree.DefaultSelectionModel = function(){
30799    this.selNode = null;
30800    
30801    this.addEvents({
30802        /**
30803         * @event selectionchange
30804         * Fires when the selected node changes
30805         * @param {DefaultSelectionModel} this
30806         * @param {TreeNode} node the new selection
30807         */
30808        "selectionchange" : true,
30809
30810        /**
30811         * @event beforeselect
30812         * Fires before the selected node changes, return false to cancel the change
30813         * @param {DefaultSelectionModel} this
30814         * @param {TreeNode} node the new selection
30815         * @param {TreeNode} node the old selection
30816         */
30817        "beforeselect" : true
30818    });
30819 };
30820
30821 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30822     init : function(tree){
30823         this.tree = tree;
30824         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30825         tree.on("click", this.onNodeClick, this);
30826     },
30827     
30828     onNodeClick : function(node, e){
30829         if (e.ctrlKey && this.selNode == node)  {
30830             this.unselect(node);
30831             return;
30832         }
30833         this.select(node);
30834     },
30835     
30836     /**
30837      * Select a node.
30838      * @param {TreeNode} node The node to select
30839      * @return {TreeNode} The selected node
30840      */
30841     select : function(node){
30842         var last = this.selNode;
30843         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30844             if(last){
30845                 last.ui.onSelectedChange(false);
30846             }
30847             this.selNode = node;
30848             node.ui.onSelectedChange(true);
30849             this.fireEvent("selectionchange", this, node, last);
30850         }
30851         return node;
30852     },
30853     
30854     /**
30855      * Deselect a node.
30856      * @param {TreeNode} node The node to unselect
30857      */
30858     unselect : function(node){
30859         if(this.selNode == node){
30860             this.clearSelections();
30861         }    
30862     },
30863     
30864     /**
30865      * Clear all selections
30866      */
30867     clearSelections : function(){
30868         var n = this.selNode;
30869         if(n){
30870             n.ui.onSelectedChange(false);
30871             this.selNode = null;
30872             this.fireEvent("selectionchange", this, null);
30873         }
30874         return n;
30875     },
30876     
30877     /**
30878      * Get the selected node
30879      * @return {TreeNode} The selected node
30880      */
30881     getSelectedNode : function(){
30882         return this.selNode;    
30883     },
30884     
30885     /**
30886      * Returns true if the node is selected
30887      * @param {TreeNode} node The node to check
30888      * @return {Boolean}
30889      */
30890     isSelected : function(node){
30891         return this.selNode == node;  
30892     },
30893
30894     /**
30895      * Selects the node above the selected node in the tree, intelligently walking the nodes
30896      * @return TreeNode The new selection
30897      */
30898     selectPrevious : function(){
30899         var s = this.selNode || this.lastSelNode;
30900         if(!s){
30901             return null;
30902         }
30903         var ps = s.previousSibling;
30904         if(ps){
30905             if(!ps.isExpanded() || ps.childNodes.length < 1){
30906                 return this.select(ps);
30907             } else{
30908                 var lc = ps.lastChild;
30909                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30910                     lc = lc.lastChild;
30911                 }
30912                 return this.select(lc);
30913             }
30914         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30915             return this.select(s.parentNode);
30916         }
30917         return null;
30918     },
30919
30920     /**
30921      * Selects the node above the selected node in the tree, intelligently walking the nodes
30922      * @return TreeNode The new selection
30923      */
30924     selectNext : function(){
30925         var s = this.selNode || this.lastSelNode;
30926         if(!s){
30927             return null;
30928         }
30929         if(s.firstChild && s.isExpanded()){
30930              return this.select(s.firstChild);
30931          }else if(s.nextSibling){
30932              return this.select(s.nextSibling);
30933          }else if(s.parentNode){
30934             var newS = null;
30935             s.parentNode.bubble(function(){
30936                 if(this.nextSibling){
30937                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
30938                     return false;
30939                 }
30940             });
30941             return newS;
30942          }
30943         return null;
30944     },
30945
30946     onKeyDown : function(e){
30947         var s = this.selNode || this.lastSelNode;
30948         // undesirable, but required
30949         var sm = this;
30950         if(!s){
30951             return;
30952         }
30953         var k = e.getKey();
30954         switch(k){
30955              case e.DOWN:
30956                  e.stopEvent();
30957                  this.selectNext();
30958              break;
30959              case e.UP:
30960                  e.stopEvent();
30961                  this.selectPrevious();
30962              break;
30963              case e.RIGHT:
30964                  e.preventDefault();
30965                  if(s.hasChildNodes()){
30966                      if(!s.isExpanded()){
30967                          s.expand();
30968                      }else if(s.firstChild){
30969                          this.select(s.firstChild, e);
30970                      }
30971                  }
30972              break;
30973              case e.LEFT:
30974                  e.preventDefault();
30975                  if(s.hasChildNodes() && s.isExpanded()){
30976                      s.collapse();
30977                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
30978                      this.select(s.parentNode, e);
30979                  }
30980              break;
30981         };
30982     }
30983 });
30984
30985 /**
30986  * @class Roo.tree.MultiSelectionModel
30987  * @extends Roo.util.Observable
30988  * Multi selection for a TreePanel.
30989  */
30990 Roo.tree.MultiSelectionModel = function(){
30991    this.selNodes = [];
30992    this.selMap = {};
30993    this.addEvents({
30994        /**
30995         * @event selectionchange
30996         * Fires when the selected nodes change
30997         * @param {MultiSelectionModel} this
30998         * @param {Array} nodes Array of the selected nodes
30999         */
31000        "selectionchange" : true
31001    });
31002 };
31003
31004 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31005     init : function(tree){
31006         this.tree = tree;
31007         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31008         tree.on("click", this.onNodeClick, this);
31009     },
31010     
31011     onNodeClick : function(node, e){
31012         this.select(node, e, e.ctrlKey);
31013     },
31014     
31015     /**
31016      * Select a node.
31017      * @param {TreeNode} node The node to select
31018      * @param {EventObject} e (optional) An event associated with the selection
31019      * @param {Boolean} keepExisting True to retain existing selections
31020      * @return {TreeNode} The selected node
31021      */
31022     select : function(node, e, keepExisting){
31023         if(keepExisting !== true){
31024             this.clearSelections(true);
31025         }
31026         if(this.isSelected(node)){
31027             this.lastSelNode = node;
31028             return node;
31029         }
31030         this.selNodes.push(node);
31031         this.selMap[node.id] = node;
31032         this.lastSelNode = node;
31033         node.ui.onSelectedChange(true);
31034         this.fireEvent("selectionchange", this, this.selNodes);
31035         return node;
31036     },
31037     
31038     /**
31039      * Deselect a node.
31040      * @param {TreeNode} node The node to unselect
31041      */
31042     unselect : function(node){
31043         if(this.selMap[node.id]){
31044             node.ui.onSelectedChange(false);
31045             var sn = this.selNodes;
31046             var index = -1;
31047             if(sn.indexOf){
31048                 index = sn.indexOf(node);
31049             }else{
31050                 for(var i = 0, len = sn.length; i < len; i++){
31051                     if(sn[i] == node){
31052                         index = i;
31053                         break;
31054                     }
31055                 }
31056             }
31057             if(index != -1){
31058                 this.selNodes.splice(index, 1);
31059             }
31060             delete this.selMap[node.id];
31061             this.fireEvent("selectionchange", this, this.selNodes);
31062         }
31063     },
31064     
31065     /**
31066      * Clear all selections
31067      */
31068     clearSelections : function(suppressEvent){
31069         var sn = this.selNodes;
31070         if(sn.length > 0){
31071             for(var i = 0, len = sn.length; i < len; i++){
31072                 sn[i].ui.onSelectedChange(false);
31073             }
31074             this.selNodes = [];
31075             this.selMap = {};
31076             if(suppressEvent !== true){
31077                 this.fireEvent("selectionchange", this, this.selNodes);
31078             }
31079         }
31080     },
31081     
31082     /**
31083      * Returns true if the node is selected
31084      * @param {TreeNode} node The node to check
31085      * @return {Boolean}
31086      */
31087     isSelected : function(node){
31088         return this.selMap[node.id] ? true : false;  
31089     },
31090     
31091     /**
31092      * Returns an array of the selected nodes
31093      * @return {Array}
31094      */
31095     getSelectedNodes : function(){
31096         return this.selNodes;    
31097     },
31098
31099     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31100
31101     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31102
31103     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31104 });/*
31105  * Based on:
31106  * Ext JS Library 1.1.1
31107  * Copyright(c) 2006-2007, Ext JS, LLC.
31108  *
31109  * Originally Released Under LGPL - original licence link has changed is not relivant.
31110  *
31111  * Fork - LGPL
31112  * <script type="text/javascript">
31113  */
31114  
31115 /**
31116  * @class Roo.tree.TreeNode
31117  * @extends Roo.data.Node
31118  * @cfg {String} text The text for this node
31119  * @cfg {Boolean} expanded true to start the node expanded
31120  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31121  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31122  * @cfg {Boolean} disabled true to start the node disabled
31123  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31124  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31125  * @cfg {String} cls A css class to be added to the node
31126  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31127  * @cfg {String} href URL of the link used for the node (defaults to #)
31128  * @cfg {String} hrefTarget target frame for the link
31129  * @cfg {String} qtip An Ext QuickTip for the node
31130  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31131  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31132  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31133  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31134  * (defaults to undefined with no checkbox rendered)
31135  * @constructor
31136  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31137  */
31138 Roo.tree.TreeNode = function(attributes){
31139     attributes = attributes || {};
31140     if(typeof attributes == "string"){
31141         attributes = {text: attributes};
31142     }
31143     this.childrenRendered = false;
31144     this.rendered = false;
31145     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31146     this.expanded = attributes.expanded === true;
31147     this.isTarget = attributes.isTarget !== false;
31148     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31149     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31150
31151     /**
31152      * Read-only. The text for this node. To change it use setText().
31153      * @type String
31154      */
31155     this.text = attributes.text;
31156     /**
31157      * True if this node is disabled.
31158      * @type Boolean
31159      */
31160     this.disabled = attributes.disabled === true;
31161
31162     this.addEvents({
31163         /**
31164         * @event textchange
31165         * Fires when the text for this node is changed
31166         * @param {Node} this This node
31167         * @param {String} text The new text
31168         * @param {String} oldText The old text
31169         */
31170         "textchange" : true,
31171         /**
31172         * @event beforeexpand
31173         * Fires before this node is expanded, return false to cancel.
31174         * @param {Node} this This node
31175         * @param {Boolean} deep
31176         * @param {Boolean} anim
31177         */
31178         "beforeexpand" : true,
31179         /**
31180         * @event beforecollapse
31181         * Fires before this node is collapsed, return false to cancel.
31182         * @param {Node} this This node
31183         * @param {Boolean} deep
31184         * @param {Boolean} anim
31185         */
31186         "beforecollapse" : true,
31187         /**
31188         * @event expand
31189         * Fires when this node is expanded
31190         * @param {Node} this This node
31191         */
31192         "expand" : true,
31193         /**
31194         * @event disabledchange
31195         * Fires when the disabled status of this node changes
31196         * @param {Node} this This node
31197         * @param {Boolean} disabled
31198         */
31199         "disabledchange" : true,
31200         /**
31201         * @event collapse
31202         * Fires when this node is collapsed
31203         * @param {Node} this This node
31204         */
31205         "collapse" : true,
31206         /**
31207         * @event beforeclick
31208         * Fires before click processing. Return false to cancel the default action.
31209         * @param {Node} this This node
31210         * @param {Roo.EventObject} e The event object
31211         */
31212         "beforeclick":true,
31213         /**
31214         * @event checkchange
31215         * Fires when a node with a checkbox's checked property changes
31216         * @param {Node} this This node
31217         * @param {Boolean} checked
31218         */
31219         "checkchange":true,
31220         /**
31221         * @event click
31222         * Fires when this node is clicked
31223         * @param {Node} this This node
31224         * @param {Roo.EventObject} e The event object
31225         */
31226         "click":true,
31227         /**
31228         * @event dblclick
31229         * Fires when this node is double clicked
31230         * @param {Node} this This node
31231         * @param {Roo.EventObject} e The event object
31232         */
31233         "dblclick":true,
31234         /**
31235         * @event contextmenu
31236         * Fires when this node is right clicked
31237         * @param {Node} this This node
31238         * @param {Roo.EventObject} e The event object
31239         */
31240         "contextmenu":true,
31241         /**
31242         * @event beforechildrenrendered
31243         * Fires right before the child nodes for this node are rendered
31244         * @param {Node} this This node
31245         */
31246         "beforechildrenrendered":true
31247     });
31248
31249     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31250
31251     /**
31252      * Read-only. The UI for this node
31253      * @type TreeNodeUI
31254      */
31255     this.ui = new uiClass(this);
31256 };
31257 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31258     preventHScroll: true,
31259     /**
31260      * Returns true if this node is expanded
31261      * @return {Boolean}
31262      */
31263     isExpanded : function(){
31264         return this.expanded;
31265     },
31266
31267     /**
31268      * Returns the UI object for this node
31269      * @return {TreeNodeUI}
31270      */
31271     getUI : function(){
31272         return this.ui;
31273     },
31274
31275     // private override
31276     setFirstChild : function(node){
31277         var of = this.firstChild;
31278         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31279         if(this.childrenRendered && of && node != of){
31280             of.renderIndent(true, true);
31281         }
31282         if(this.rendered){
31283             this.renderIndent(true, true);
31284         }
31285     },
31286
31287     // private override
31288     setLastChild : function(node){
31289         var ol = this.lastChild;
31290         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31291         if(this.childrenRendered && ol && node != ol){
31292             ol.renderIndent(true, true);
31293         }
31294         if(this.rendered){
31295             this.renderIndent(true, true);
31296         }
31297     },
31298
31299     // these methods are overridden to provide lazy rendering support
31300     // private override
31301     appendChild : function(){
31302         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31303         if(node && this.childrenRendered){
31304             node.render();
31305         }
31306         this.ui.updateExpandIcon();
31307         return node;
31308     },
31309
31310     // private override
31311     removeChild : function(node){
31312         this.ownerTree.getSelectionModel().unselect(node);
31313         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31314         // if it's been rendered remove dom node
31315         if(this.childrenRendered){
31316             node.ui.remove();
31317         }
31318         if(this.childNodes.length < 1){
31319             this.collapse(false, false);
31320         }else{
31321             this.ui.updateExpandIcon();
31322         }
31323         if(!this.firstChild) {
31324             this.childrenRendered = false;
31325         }
31326         return node;
31327     },
31328
31329     // private override
31330     insertBefore : function(node, refNode){
31331         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31332         if(newNode && refNode && this.childrenRendered){
31333             node.render();
31334         }
31335         this.ui.updateExpandIcon();
31336         return newNode;
31337     },
31338
31339     /**
31340      * Sets the text for this node
31341      * @param {String} text
31342      */
31343     setText : function(text){
31344         var oldText = this.text;
31345         this.text = text;
31346         this.attributes.text = text;
31347         if(this.rendered){ // event without subscribing
31348             this.ui.onTextChange(this, text, oldText);
31349         }
31350         this.fireEvent("textchange", this, text, oldText);
31351     },
31352
31353     /**
31354      * Triggers selection of this node
31355      */
31356     select : function(){
31357         this.getOwnerTree().getSelectionModel().select(this);
31358     },
31359
31360     /**
31361      * Triggers deselection of this node
31362      */
31363     unselect : function(){
31364         this.getOwnerTree().getSelectionModel().unselect(this);
31365     },
31366
31367     /**
31368      * Returns true if this node is selected
31369      * @return {Boolean}
31370      */
31371     isSelected : function(){
31372         return this.getOwnerTree().getSelectionModel().isSelected(this);
31373     },
31374
31375     /**
31376      * Expand this node.
31377      * @param {Boolean} deep (optional) True to expand all children as well
31378      * @param {Boolean} anim (optional) false to cancel the default animation
31379      * @param {Function} callback (optional) A callback to be called when
31380      * expanding this node completes (does not wait for deep expand to complete).
31381      * Called with 1 parameter, this node.
31382      */
31383     expand : function(deep, anim, callback){
31384         if(!this.expanded){
31385             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31386                 return;
31387             }
31388             if(!this.childrenRendered){
31389                 this.renderChildren();
31390             }
31391             this.expanded = true;
31392             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31393                 this.ui.animExpand(function(){
31394                     this.fireEvent("expand", this);
31395                     if(typeof callback == "function"){
31396                         callback(this);
31397                     }
31398                     if(deep === true){
31399                         this.expandChildNodes(true);
31400                     }
31401                 }.createDelegate(this));
31402                 return;
31403             }else{
31404                 this.ui.expand();
31405                 this.fireEvent("expand", this);
31406                 if(typeof callback == "function"){
31407                     callback(this);
31408                 }
31409             }
31410         }else{
31411            if(typeof callback == "function"){
31412                callback(this);
31413            }
31414         }
31415         if(deep === true){
31416             this.expandChildNodes(true);
31417         }
31418     },
31419
31420     isHiddenRoot : function(){
31421         return this.isRoot && !this.getOwnerTree().rootVisible;
31422     },
31423
31424     /**
31425      * Collapse this node.
31426      * @param {Boolean} deep (optional) True to collapse all children as well
31427      * @param {Boolean} anim (optional) false to cancel the default animation
31428      */
31429     collapse : function(deep, anim){
31430         if(this.expanded && !this.isHiddenRoot()){
31431             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31432                 return;
31433             }
31434             this.expanded = false;
31435             if((this.getOwnerTree().animate && anim !== false) || anim){
31436                 this.ui.animCollapse(function(){
31437                     this.fireEvent("collapse", this);
31438                     if(deep === true){
31439                         this.collapseChildNodes(true);
31440                     }
31441                 }.createDelegate(this));
31442                 return;
31443             }else{
31444                 this.ui.collapse();
31445                 this.fireEvent("collapse", this);
31446             }
31447         }
31448         if(deep === true){
31449             var cs = this.childNodes;
31450             for(var i = 0, len = cs.length; i < len; i++) {
31451                 cs[i].collapse(true, false);
31452             }
31453         }
31454     },
31455
31456     // private
31457     delayedExpand : function(delay){
31458         if(!this.expandProcId){
31459             this.expandProcId = this.expand.defer(delay, this);
31460         }
31461     },
31462
31463     // private
31464     cancelExpand : function(){
31465         if(this.expandProcId){
31466             clearTimeout(this.expandProcId);
31467         }
31468         this.expandProcId = false;
31469     },
31470
31471     /**
31472      * Toggles expanded/collapsed state of the node
31473      */
31474     toggle : function(){
31475         if(this.expanded){
31476             this.collapse();
31477         }else{
31478             this.expand();
31479         }
31480     },
31481
31482     /**
31483      * Ensures all parent nodes are expanded
31484      */
31485     ensureVisible : function(callback){
31486         var tree = this.getOwnerTree();
31487         tree.expandPath(this.parentNode.getPath(), false, function(){
31488             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31489             Roo.callback(callback);
31490         }.createDelegate(this));
31491     },
31492
31493     /**
31494      * Expand all child nodes
31495      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31496      */
31497     expandChildNodes : function(deep){
31498         var cs = this.childNodes;
31499         for(var i = 0, len = cs.length; i < len; i++) {
31500                 cs[i].expand(deep);
31501         }
31502     },
31503
31504     /**
31505      * Collapse all child nodes
31506      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31507      */
31508     collapseChildNodes : function(deep){
31509         var cs = this.childNodes;
31510         for(var i = 0, len = cs.length; i < len; i++) {
31511                 cs[i].collapse(deep);
31512         }
31513     },
31514
31515     /**
31516      * Disables this node
31517      */
31518     disable : function(){
31519         this.disabled = true;
31520         this.unselect();
31521         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31522             this.ui.onDisableChange(this, true);
31523         }
31524         this.fireEvent("disabledchange", this, true);
31525     },
31526
31527     /**
31528      * Enables this node
31529      */
31530     enable : function(){
31531         this.disabled = false;
31532         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31533             this.ui.onDisableChange(this, false);
31534         }
31535         this.fireEvent("disabledchange", this, false);
31536     },
31537
31538     // private
31539     renderChildren : function(suppressEvent){
31540         if(suppressEvent !== false){
31541             this.fireEvent("beforechildrenrendered", this);
31542         }
31543         var cs = this.childNodes;
31544         for(var i = 0, len = cs.length; i < len; i++){
31545             cs[i].render(true);
31546         }
31547         this.childrenRendered = true;
31548     },
31549
31550     // private
31551     sort : function(fn, scope){
31552         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31553         if(this.childrenRendered){
31554             var cs = this.childNodes;
31555             for(var i = 0, len = cs.length; i < len; i++){
31556                 cs[i].render(true);
31557             }
31558         }
31559     },
31560
31561     // private
31562     render : function(bulkRender){
31563         this.ui.render(bulkRender);
31564         if(!this.rendered){
31565             this.rendered = true;
31566             if(this.expanded){
31567                 this.expanded = false;
31568                 this.expand(false, false);
31569             }
31570         }
31571     },
31572
31573     // private
31574     renderIndent : function(deep, refresh){
31575         if(refresh){
31576             this.ui.childIndent = null;
31577         }
31578         this.ui.renderIndent();
31579         if(deep === true && this.childrenRendered){
31580             var cs = this.childNodes;
31581             for(var i = 0, len = cs.length; i < len; i++){
31582                 cs[i].renderIndent(true, refresh);
31583             }
31584         }
31585     }
31586 });/*
31587  * Based on:
31588  * Ext JS Library 1.1.1
31589  * Copyright(c) 2006-2007, Ext JS, LLC.
31590  *
31591  * Originally Released Under LGPL - original licence link has changed is not relivant.
31592  *
31593  * Fork - LGPL
31594  * <script type="text/javascript">
31595  */
31596  
31597 /**
31598  * @class Roo.tree.AsyncTreeNode
31599  * @extends Roo.tree.TreeNode
31600  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31601  * @constructor
31602  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31603  */
31604  Roo.tree.AsyncTreeNode = function(config){
31605     this.loaded = false;
31606     this.loading = false;
31607     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31608     /**
31609     * @event beforeload
31610     * Fires before this node is loaded, return false to cancel
31611     * @param {Node} this This node
31612     */
31613     this.addEvents({'beforeload':true, 'load': true});
31614     /**
31615     * @event load
31616     * Fires when this node is loaded
31617     * @param {Node} this This node
31618     */
31619     /**
31620      * The loader used by this node (defaults to using the tree's defined loader)
31621      * @type TreeLoader
31622      * @property loader
31623      */
31624 };
31625 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31626     expand : function(deep, anim, callback){
31627         if(this.loading){ // if an async load is already running, waiting til it's done
31628             var timer;
31629             var f = function(){
31630                 if(!this.loading){ // done loading
31631                     clearInterval(timer);
31632                     this.expand(deep, anim, callback);
31633                 }
31634             }.createDelegate(this);
31635             timer = setInterval(f, 200);
31636             return;
31637         }
31638         if(!this.loaded){
31639             if(this.fireEvent("beforeload", this) === false){
31640                 return;
31641             }
31642             this.loading = true;
31643             this.ui.beforeLoad(this);
31644             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31645             if(loader){
31646                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31647                 return;
31648             }
31649         }
31650         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31651     },
31652     
31653     /**
31654      * Returns true if this node is currently loading
31655      * @return {Boolean}
31656      */
31657     isLoading : function(){
31658         return this.loading;  
31659     },
31660     
31661     loadComplete : function(deep, anim, callback){
31662         this.loading = false;
31663         this.loaded = true;
31664         this.ui.afterLoad(this);
31665         this.fireEvent("load", this);
31666         this.expand(deep, anim, callback);
31667     },
31668     
31669     /**
31670      * Returns true if this node has been loaded
31671      * @return {Boolean}
31672      */
31673     isLoaded : function(){
31674         return this.loaded;
31675     },
31676     
31677     hasChildNodes : function(){
31678         if(!this.isLeaf() && !this.loaded){
31679             return true;
31680         }else{
31681             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31682         }
31683     },
31684
31685     /**
31686      * Trigger a reload for this node
31687      * @param {Function} callback
31688      */
31689     reload : function(callback){
31690         this.collapse(false, false);
31691         while(this.firstChild){
31692             this.removeChild(this.firstChild);
31693         }
31694         this.childrenRendered = false;
31695         this.loaded = false;
31696         if(this.isHiddenRoot()){
31697             this.expanded = false;
31698         }
31699         this.expand(false, false, callback);
31700     }
31701 });/*
31702  * Based on:
31703  * Ext JS Library 1.1.1
31704  * Copyright(c) 2006-2007, Ext JS, LLC.
31705  *
31706  * Originally Released Under LGPL - original licence link has changed is not relivant.
31707  *
31708  * Fork - LGPL
31709  * <script type="text/javascript">
31710  */
31711  
31712 /**
31713  * @class Roo.tree.TreeNodeUI
31714  * @constructor
31715  * @param {Object} node The node to render
31716  * The TreeNode UI implementation is separate from the
31717  * tree implementation. Unless you are customizing the tree UI,
31718  * you should never have to use this directly.
31719  */
31720 Roo.tree.TreeNodeUI = function(node){
31721     this.node = node;
31722     this.rendered = false;
31723     this.animating = false;
31724     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31725 };
31726
31727 Roo.tree.TreeNodeUI.prototype = {
31728     removeChild : function(node){
31729         if(this.rendered){
31730             this.ctNode.removeChild(node.ui.getEl());
31731         }
31732     },
31733
31734     beforeLoad : function(){
31735          this.addClass("x-tree-node-loading");
31736     },
31737
31738     afterLoad : function(){
31739          this.removeClass("x-tree-node-loading");
31740     },
31741
31742     onTextChange : function(node, text, oldText){
31743         if(this.rendered){
31744             this.textNode.innerHTML = text;
31745         }
31746     },
31747
31748     onDisableChange : function(node, state){
31749         this.disabled = state;
31750         if(state){
31751             this.addClass("x-tree-node-disabled");
31752         }else{
31753             this.removeClass("x-tree-node-disabled");
31754         }
31755     },
31756
31757     onSelectedChange : function(state){
31758         if(state){
31759             this.focus();
31760             this.addClass("x-tree-selected");
31761         }else{
31762             //this.blur();
31763             this.removeClass("x-tree-selected");
31764         }
31765     },
31766
31767     onMove : function(tree, node, oldParent, newParent, index, refNode){
31768         this.childIndent = null;
31769         if(this.rendered){
31770             var targetNode = newParent.ui.getContainer();
31771             if(!targetNode){//target not rendered
31772                 this.holder = document.createElement("div");
31773                 this.holder.appendChild(this.wrap);
31774                 return;
31775             }
31776             var insertBefore = refNode ? refNode.ui.getEl() : null;
31777             if(insertBefore){
31778                 targetNode.insertBefore(this.wrap, insertBefore);
31779             }else{
31780                 targetNode.appendChild(this.wrap);
31781             }
31782             this.node.renderIndent(true);
31783         }
31784     },
31785
31786     addClass : function(cls){
31787         if(this.elNode){
31788             Roo.fly(this.elNode).addClass(cls);
31789         }
31790     },
31791
31792     removeClass : function(cls){
31793         if(this.elNode){
31794             Roo.fly(this.elNode).removeClass(cls);
31795         }
31796     },
31797
31798     remove : function(){
31799         if(this.rendered){
31800             this.holder = document.createElement("div");
31801             this.holder.appendChild(this.wrap);
31802         }
31803     },
31804
31805     fireEvent : function(){
31806         return this.node.fireEvent.apply(this.node, arguments);
31807     },
31808
31809     initEvents : function(){
31810         this.node.on("move", this.onMove, this);
31811         var E = Roo.EventManager;
31812         var a = this.anchor;
31813
31814         var el = Roo.fly(a, '_treeui');
31815
31816         if(Roo.isOpera){ // opera render bug ignores the CSS
31817             el.setStyle("text-decoration", "none");
31818         }
31819
31820         el.on("click", this.onClick, this);
31821         el.on("dblclick", this.onDblClick, this);
31822
31823         if(this.checkbox){
31824             Roo.EventManager.on(this.checkbox,
31825                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31826         }
31827
31828         el.on("contextmenu", this.onContextMenu, this);
31829
31830         var icon = Roo.fly(this.iconNode);
31831         icon.on("click", this.onClick, this);
31832         icon.on("dblclick", this.onDblClick, this);
31833         icon.on("contextmenu", this.onContextMenu, this);
31834         E.on(this.ecNode, "click", this.ecClick, this, true);
31835
31836         if(this.node.disabled){
31837             this.addClass("x-tree-node-disabled");
31838         }
31839         if(this.node.hidden){
31840             this.addClass("x-tree-node-disabled");
31841         }
31842         var ot = this.node.getOwnerTree();
31843         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31844         if(dd && (!this.node.isRoot || ot.rootVisible)){
31845             Roo.dd.Registry.register(this.elNode, {
31846                 node: this.node,
31847                 handles: this.getDDHandles(),
31848                 isHandle: false
31849             });
31850         }
31851     },
31852
31853     getDDHandles : function(){
31854         return [this.iconNode, this.textNode];
31855     },
31856
31857     hide : function(){
31858         if(this.rendered){
31859             this.wrap.style.display = "none";
31860         }
31861     },
31862
31863     show : function(){
31864         if(this.rendered){
31865             this.wrap.style.display = "";
31866         }
31867     },
31868
31869     onContextMenu : function(e){
31870         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31871             e.preventDefault();
31872             this.focus();
31873             this.fireEvent("contextmenu", this.node, e);
31874         }
31875     },
31876
31877     onClick : function(e){
31878         if(this.dropping){
31879             e.stopEvent();
31880             return;
31881         }
31882         if(this.fireEvent("beforeclick", this.node, e) !== false){
31883             if(!this.disabled && this.node.attributes.href){
31884                 this.fireEvent("click", this.node, e);
31885                 return;
31886             }
31887             e.preventDefault();
31888             if(this.disabled){
31889                 return;
31890             }
31891
31892             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31893                 this.node.toggle();
31894             }
31895
31896             this.fireEvent("click", this.node, e);
31897         }else{
31898             e.stopEvent();
31899         }
31900     },
31901
31902     onDblClick : function(e){
31903         e.preventDefault();
31904         if(this.disabled){
31905             return;
31906         }
31907         if(this.checkbox){
31908             this.toggleCheck();
31909         }
31910         if(!this.animating && this.node.hasChildNodes()){
31911             this.node.toggle();
31912         }
31913         this.fireEvent("dblclick", this.node, e);
31914     },
31915
31916     onCheckChange : function(){
31917         var checked = this.checkbox.checked;
31918         this.node.attributes.checked = checked;
31919         this.fireEvent('checkchange', this.node, checked);
31920     },
31921
31922     ecClick : function(e){
31923         if(!this.animating && this.node.hasChildNodes()){
31924             this.node.toggle();
31925         }
31926     },
31927
31928     startDrop : function(){
31929         this.dropping = true;
31930     },
31931
31932     // delayed drop so the click event doesn't get fired on a drop
31933     endDrop : function(){
31934        setTimeout(function(){
31935            this.dropping = false;
31936        }.createDelegate(this), 50);
31937     },
31938
31939     expand : function(){
31940         this.updateExpandIcon();
31941         this.ctNode.style.display = "";
31942     },
31943
31944     focus : function(){
31945         if(!this.node.preventHScroll){
31946             try{this.anchor.focus();
31947             }catch(e){}
31948         }else if(!Roo.isIE){
31949             try{
31950                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
31951                 var l = noscroll.scrollLeft;
31952                 this.anchor.focus();
31953                 noscroll.scrollLeft = l;
31954             }catch(e){}
31955         }
31956     },
31957
31958     toggleCheck : function(value){
31959         var cb = this.checkbox;
31960         if(cb){
31961             cb.checked = (value === undefined ? !cb.checked : value);
31962         }
31963     },
31964
31965     blur : function(){
31966         try{
31967             this.anchor.blur();
31968         }catch(e){}
31969     },
31970
31971     animExpand : function(callback){
31972         var ct = Roo.get(this.ctNode);
31973         ct.stopFx();
31974         if(!this.node.hasChildNodes()){
31975             this.updateExpandIcon();
31976             this.ctNode.style.display = "";
31977             Roo.callback(callback);
31978             return;
31979         }
31980         this.animating = true;
31981         this.updateExpandIcon();
31982
31983         ct.slideIn('t', {
31984            callback : function(){
31985                this.animating = false;
31986                Roo.callback(callback);
31987             },
31988             scope: this,
31989             duration: this.node.ownerTree.duration || .25
31990         });
31991     },
31992
31993     highlight : function(){
31994         var tree = this.node.getOwnerTree();
31995         Roo.fly(this.wrap).highlight(
31996             tree.hlColor || "C3DAF9",
31997             {endColor: tree.hlBaseColor}
31998         );
31999     },
32000
32001     collapse : function(){
32002         this.updateExpandIcon();
32003         this.ctNode.style.display = "none";
32004     },
32005
32006     animCollapse : function(callback){
32007         var ct = Roo.get(this.ctNode);
32008         ct.enableDisplayMode('block');
32009         ct.stopFx();
32010
32011         this.animating = true;
32012         this.updateExpandIcon();
32013
32014         ct.slideOut('t', {
32015             callback : function(){
32016                this.animating = false;
32017                Roo.callback(callback);
32018             },
32019             scope: this,
32020             duration: this.node.ownerTree.duration || .25
32021         });
32022     },
32023
32024     getContainer : function(){
32025         return this.ctNode;
32026     },
32027
32028     getEl : function(){
32029         return this.wrap;
32030     },
32031
32032     appendDDGhost : function(ghostNode){
32033         ghostNode.appendChild(this.elNode.cloneNode(true));
32034     },
32035
32036     getDDRepairXY : function(){
32037         return Roo.lib.Dom.getXY(this.iconNode);
32038     },
32039
32040     onRender : function(){
32041         this.render();
32042     },
32043
32044     render : function(bulkRender){
32045         var n = this.node, a = n.attributes;
32046         var targetNode = n.parentNode ?
32047               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32048
32049         if(!this.rendered){
32050             this.rendered = true;
32051
32052             this.renderElements(n, a, targetNode, bulkRender);
32053
32054             if(a.qtip){
32055                if(this.textNode.setAttributeNS){
32056                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32057                    if(a.qtipTitle){
32058                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32059                    }
32060                }else{
32061                    this.textNode.setAttribute("ext:qtip", a.qtip);
32062                    if(a.qtipTitle){
32063                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32064                    }
32065                }
32066             }else if(a.qtipCfg){
32067                 a.qtipCfg.target = Roo.id(this.textNode);
32068                 Roo.QuickTips.register(a.qtipCfg);
32069             }
32070             this.initEvents();
32071             if(!this.node.expanded){
32072                 this.updateExpandIcon();
32073             }
32074         }else{
32075             if(bulkRender === true) {
32076                 targetNode.appendChild(this.wrap);
32077             }
32078         }
32079     },
32080
32081     renderElements : function(n, a, targetNode, bulkRender){
32082         // add some indent caching, this helps performance when rendering a large tree
32083         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32084         var t = n.getOwnerTree();
32085         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32086         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32087         var cb = typeof a.checked == 'boolean';
32088         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32089         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32090             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32091             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32092             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32093             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32094             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32095              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32096                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32097             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32098             "</li>"];
32099
32100         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32101             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32102                                 n.nextSibling.ui.getEl(), buf.join(""));
32103         }else{
32104             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32105         }
32106
32107         this.elNode = this.wrap.childNodes[0];
32108         this.ctNode = this.wrap.childNodes[1];
32109         var cs = this.elNode.childNodes;
32110         this.indentNode = cs[0];
32111         this.ecNode = cs[1];
32112         this.iconNode = cs[2];
32113         var index = 3;
32114         if(cb){
32115             this.checkbox = cs[3];
32116             index++;
32117         }
32118         this.anchor = cs[index];
32119         this.textNode = cs[index].firstChild;
32120     },
32121
32122     getAnchor : function(){
32123         return this.anchor;
32124     },
32125
32126     getTextEl : function(){
32127         return this.textNode;
32128     },
32129
32130     getIconEl : function(){
32131         return this.iconNode;
32132     },
32133
32134     isChecked : function(){
32135         return this.checkbox ? this.checkbox.checked : false;
32136     },
32137
32138     updateExpandIcon : function(){
32139         if(this.rendered){
32140             var n = this.node, c1, c2;
32141             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32142             var hasChild = n.hasChildNodes();
32143             if(hasChild){
32144                 if(n.expanded){
32145                     cls += "-minus";
32146                     c1 = "x-tree-node-collapsed";
32147                     c2 = "x-tree-node-expanded";
32148                 }else{
32149                     cls += "-plus";
32150                     c1 = "x-tree-node-expanded";
32151                     c2 = "x-tree-node-collapsed";
32152                 }
32153                 if(this.wasLeaf){
32154                     this.removeClass("x-tree-node-leaf");
32155                     this.wasLeaf = false;
32156                 }
32157                 if(this.c1 != c1 || this.c2 != c2){
32158                     Roo.fly(this.elNode).replaceClass(c1, c2);
32159                     this.c1 = c1; this.c2 = c2;
32160                 }
32161             }else{
32162                 if(!this.wasLeaf){
32163                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32164                     delete this.c1;
32165                     delete this.c2;
32166                     this.wasLeaf = true;
32167                 }
32168             }
32169             var ecc = "x-tree-ec-icon "+cls;
32170             if(this.ecc != ecc){
32171                 this.ecNode.className = ecc;
32172                 this.ecc = ecc;
32173             }
32174         }
32175     },
32176
32177     getChildIndent : function(){
32178         if(!this.childIndent){
32179             var buf = [];
32180             var p = this.node;
32181             while(p){
32182                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32183                     if(!p.isLast()) {
32184                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32185                     } else {
32186                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32187                     }
32188                 }
32189                 p = p.parentNode;
32190             }
32191             this.childIndent = buf.join("");
32192         }
32193         return this.childIndent;
32194     },
32195
32196     renderIndent : function(){
32197         if(this.rendered){
32198             var indent = "";
32199             var p = this.node.parentNode;
32200             if(p){
32201                 indent = p.ui.getChildIndent();
32202             }
32203             if(this.indentMarkup != indent){ // don't rerender if not required
32204                 this.indentNode.innerHTML = indent;
32205                 this.indentMarkup = indent;
32206             }
32207             this.updateExpandIcon();
32208         }
32209     }
32210 };
32211
32212 Roo.tree.RootTreeNodeUI = function(){
32213     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32214 };
32215 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32216     render : function(){
32217         if(!this.rendered){
32218             var targetNode = this.node.ownerTree.innerCt.dom;
32219             this.node.expanded = true;
32220             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32221             this.wrap = this.ctNode = targetNode.firstChild;
32222         }
32223     },
32224     collapse : function(){
32225     },
32226     expand : function(){
32227     }
32228 });/*
32229  * Based on:
32230  * Ext JS Library 1.1.1
32231  * Copyright(c) 2006-2007, Ext JS, LLC.
32232  *
32233  * Originally Released Under LGPL - original licence link has changed is not relivant.
32234  *
32235  * Fork - LGPL
32236  * <script type="text/javascript">
32237  */
32238 /**
32239  * @class Roo.tree.TreeLoader
32240  * @extends Roo.util.Observable
32241  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32242  * nodes from a specified URL. The response must be a javascript Array definition
32243  * who's elements are node definition objects. eg:
32244  * <pre><code>
32245    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32246     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32247 </code></pre>
32248  * <br><br>
32249  * A server request is sent, and child nodes are loaded only when a node is expanded.
32250  * The loading node's id is passed to the server under the parameter name "node" to
32251  * enable the server to produce the correct child nodes.
32252  * <br><br>
32253  * To pass extra parameters, an event handler may be attached to the "beforeload"
32254  * event, and the parameters specified in the TreeLoader's baseParams property:
32255  * <pre><code>
32256     myTreeLoader.on("beforeload", function(treeLoader, node) {
32257         this.baseParams.category = node.attributes.category;
32258     }, this);
32259 </code></pre><
32260  * This would pass an HTTP parameter called "category" to the server containing
32261  * the value of the Node's "category" attribute.
32262  * @constructor
32263  * Creates a new Treeloader.
32264  * @param {Object} config A config object containing config properties.
32265  */
32266 Roo.tree.TreeLoader = function(config){
32267     this.baseParams = {};
32268     this.requestMethod = "POST";
32269     Roo.apply(this, config);
32270
32271     this.addEvents({
32272     
32273         /**
32274          * @event beforeload
32275          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32276          * @param {Object} This TreeLoader object.
32277          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32278          * @param {Object} callback The callback function specified in the {@link #load} call.
32279          */
32280         beforeload : true,
32281         /**
32282          * @event load
32283          * Fires when the node has been successfuly loaded.
32284          * @param {Object} This TreeLoader object.
32285          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32286          * @param {Object} response The response object containing the data from the server.
32287          */
32288         load : true,
32289         /**
32290          * @event loadexception
32291          * Fires if the network request failed.
32292          * @param {Object} This TreeLoader object.
32293          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32294          * @param {Object} response The response object containing the data from the server.
32295          */
32296         loadexception : true,
32297         /**
32298          * @event create
32299          * Fires before a node is created, enabling you to return custom Node types 
32300          * @param {Object} This TreeLoader object.
32301          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32302          */
32303         create : true
32304     });
32305
32306     Roo.tree.TreeLoader.superclass.constructor.call(this);
32307 };
32308
32309 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32310     /**
32311     * @cfg {String} dataUrl The URL from which to request a Json string which
32312     * specifies an array of node definition object representing the child nodes
32313     * to be loaded.
32314     */
32315     /**
32316     * @cfg {Object} baseParams (optional) An object containing properties which
32317     * specify HTTP parameters to be passed to each request for child nodes.
32318     */
32319     /**
32320     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32321     * created by this loader. If the attributes sent by the server have an attribute in this object,
32322     * they take priority.
32323     */
32324     /**
32325     * @cfg {Object} uiProviders (optional) An object containing properties which
32326     * 
32327     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32328     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32329     * <i>uiProvider</i> attribute of a returned child node is a string rather
32330     * than a reference to a TreeNodeUI implementation, this that string value
32331     * is used as a property name in the uiProviders object. You can define the provider named
32332     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32333     */
32334     uiProviders : {},
32335
32336     /**
32337     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32338     * child nodes before loading.
32339     */
32340     clearOnLoad : true,
32341
32342     /**
32343     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32344     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32345     * Grid query { data : [ .....] }
32346     */
32347     
32348     root : false,
32349      /**
32350     * @cfg {String} queryParam (optional) 
32351     * Name of the query as it will be passed on the querystring (defaults to 'node')
32352     * eg. the request will be ?node=[id]
32353     */
32354     
32355     
32356     queryParam: false,
32357     
32358     /**
32359      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32360      * This is called automatically when a node is expanded, but may be used to reload
32361      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32362      * @param {Roo.tree.TreeNode} node
32363      * @param {Function} callback
32364      */
32365     load : function(node, callback){
32366         if(this.clearOnLoad){
32367             while(node.firstChild){
32368                 node.removeChild(node.firstChild);
32369             }
32370         }
32371         if(node.attributes.children){ // preloaded json children
32372             var cs = node.attributes.children;
32373             for(var i = 0, len = cs.length; i < len; i++){
32374                 node.appendChild(this.createNode(cs[i]));
32375             }
32376             if(typeof callback == "function"){
32377                 callback();
32378             }
32379         }else if(this.dataUrl){
32380             this.requestData(node, callback);
32381         }
32382     },
32383
32384     getParams: function(node){
32385         var buf = [], bp = this.baseParams;
32386         for(var key in bp){
32387             if(typeof bp[key] != "function"){
32388                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32389             }
32390         }
32391         var n = this.queryParam === false ? 'node' : this.queryParam;
32392         buf.push(n + "=", encodeURIComponent(node.id));
32393         return buf.join("");
32394     },
32395
32396     requestData : function(node, callback){
32397         if(this.fireEvent("beforeload", this, node, callback) !== false){
32398             this.transId = Roo.Ajax.request({
32399                 method:this.requestMethod,
32400                 url: this.dataUrl||this.url,
32401                 success: this.handleResponse,
32402                 failure: this.handleFailure,
32403                 scope: this,
32404                 argument: {callback: callback, node: node},
32405                 params: this.getParams(node)
32406             });
32407         }else{
32408             // if the load is cancelled, make sure we notify
32409             // the node that we are done
32410             if(typeof callback == "function"){
32411                 callback();
32412             }
32413         }
32414     },
32415
32416     isLoading : function(){
32417         return this.transId ? true : false;
32418     },
32419
32420     abort : function(){
32421         if(this.isLoading()){
32422             Roo.Ajax.abort(this.transId);
32423         }
32424     },
32425
32426     // private
32427     createNode : function(attr){
32428         // apply baseAttrs, nice idea Corey!
32429         if(this.baseAttrs){
32430             Roo.applyIf(attr, this.baseAttrs);
32431         }
32432         if(this.applyLoader !== false){
32433             attr.loader = this;
32434         }
32435         // uiProvider = depreciated..
32436         
32437         if(typeof(attr.uiProvider) == 'string'){
32438            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32439                 /**  eval:var:attr */ eval(attr.uiProvider);
32440         }
32441         if(typeof(this.uiProviders['default']) != 'undefined') {
32442             attr.uiProvider = this.uiProviders['default'];
32443         }
32444         
32445         this.fireEvent('create', this, attr);
32446         
32447         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32448         return(attr.leaf ?
32449                         new Roo.tree.TreeNode(attr) :
32450                         new Roo.tree.AsyncTreeNode(attr));
32451     },
32452
32453     processResponse : function(response, node, callback){
32454         var json = response.responseText;
32455         try {
32456             
32457             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32458             if (this.root !== false) {
32459                 o = o[this.root];
32460             }
32461             
32462             for(var i = 0, len = o.length; i < len; i++){
32463                 var n = this.createNode(o[i]);
32464                 if(n){
32465                     node.appendChild(n);
32466                 }
32467             }
32468             if(typeof callback == "function"){
32469                 callback(this, node);
32470             }
32471         }catch(e){
32472             this.handleFailure(response);
32473         }
32474     },
32475
32476     handleResponse : function(response){
32477         this.transId = false;
32478         var a = response.argument;
32479         this.processResponse(response, a.node, a.callback);
32480         this.fireEvent("load", this, a.node, response);
32481     },
32482
32483     handleFailure : function(response){
32484         this.transId = false;
32485         var a = response.argument;
32486         this.fireEvent("loadexception", this, a.node, response);
32487         if(typeof a.callback == "function"){
32488             a.callback(this, a.node);
32489         }
32490     }
32491 });/*
32492  * Based on:
32493  * Ext JS Library 1.1.1
32494  * Copyright(c) 2006-2007, Ext JS, LLC.
32495  *
32496  * Originally Released Under LGPL - original licence link has changed is not relivant.
32497  *
32498  * Fork - LGPL
32499  * <script type="text/javascript">
32500  */
32501
32502 /**
32503 * @class Roo.tree.TreeFilter
32504 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32505 * @param {TreePanel} tree
32506 * @param {Object} config (optional)
32507  */
32508 Roo.tree.TreeFilter = function(tree, config){
32509     this.tree = tree;
32510     this.filtered = {};
32511     Roo.apply(this, config);
32512 };
32513
32514 Roo.tree.TreeFilter.prototype = {
32515     clearBlank:false,
32516     reverse:false,
32517     autoClear:false,
32518     remove:false,
32519
32520      /**
32521      * Filter the data by a specific attribute.
32522      * @param {String/RegExp} value Either string that the attribute value
32523      * should start with or a RegExp to test against the attribute
32524      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32525      * @param {TreeNode} startNode (optional) The node to start the filter at.
32526      */
32527     filter : function(value, attr, startNode){
32528         attr = attr || "text";
32529         var f;
32530         if(typeof value == "string"){
32531             var vlen = value.length;
32532             // auto clear empty filter
32533             if(vlen == 0 && this.clearBlank){
32534                 this.clear();
32535                 return;
32536             }
32537             value = value.toLowerCase();
32538             f = function(n){
32539                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32540             };
32541         }else if(value.exec){ // regex?
32542             f = function(n){
32543                 return value.test(n.attributes[attr]);
32544             };
32545         }else{
32546             throw 'Illegal filter type, must be string or regex';
32547         }
32548         this.filterBy(f, null, startNode);
32549         },
32550
32551     /**
32552      * Filter by a function. The passed function will be called with each
32553      * node in the tree (or from the startNode). If the function returns true, the node is kept
32554      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32555      * @param {Function} fn The filter function
32556      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32557      */
32558     filterBy : function(fn, scope, startNode){
32559         startNode = startNode || this.tree.root;
32560         if(this.autoClear){
32561             this.clear();
32562         }
32563         var af = this.filtered, rv = this.reverse;
32564         var f = function(n){
32565             if(n == startNode){
32566                 return true;
32567             }
32568             if(af[n.id]){
32569                 return false;
32570             }
32571             var m = fn.call(scope || n, n);
32572             if(!m || rv){
32573                 af[n.id] = n;
32574                 n.ui.hide();
32575                 return false;
32576             }
32577             return true;
32578         };
32579         startNode.cascade(f);
32580         if(this.remove){
32581            for(var id in af){
32582                if(typeof id != "function"){
32583                    var n = af[id];
32584                    if(n && n.parentNode){
32585                        n.parentNode.removeChild(n);
32586                    }
32587                }
32588            }
32589         }
32590     },
32591
32592     /**
32593      * Clears the current filter. Note: with the "remove" option
32594      * set a filter cannot be cleared.
32595      */
32596     clear : function(){
32597         var t = this.tree;
32598         var af = this.filtered;
32599         for(var id in af){
32600             if(typeof id != "function"){
32601                 var n = af[id];
32602                 if(n){
32603                     n.ui.show();
32604                 }
32605             }
32606         }
32607         this.filtered = {};
32608     }
32609 };
32610 /*
32611  * Based on:
32612  * Ext JS Library 1.1.1
32613  * Copyright(c) 2006-2007, Ext JS, LLC.
32614  *
32615  * Originally Released Under LGPL - original licence link has changed is not relivant.
32616  *
32617  * Fork - LGPL
32618  * <script type="text/javascript">
32619  */
32620  
32621
32622 /**
32623  * @class Roo.tree.TreeSorter
32624  * Provides sorting of nodes in a TreePanel
32625  * 
32626  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32627  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32628  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32629  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32630  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32631  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32632  * @constructor
32633  * @param {TreePanel} tree
32634  * @param {Object} config
32635  */
32636 Roo.tree.TreeSorter = function(tree, config){
32637     Roo.apply(this, config);
32638     tree.on("beforechildrenrendered", this.doSort, this);
32639     tree.on("append", this.updateSort, this);
32640     tree.on("insert", this.updateSort, this);
32641     
32642     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32643     var p = this.property || "text";
32644     var sortType = this.sortType;
32645     var fs = this.folderSort;
32646     var cs = this.caseSensitive === true;
32647     var leafAttr = this.leafAttr || 'leaf';
32648
32649     this.sortFn = function(n1, n2){
32650         if(fs){
32651             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32652                 return 1;
32653             }
32654             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32655                 return -1;
32656             }
32657         }
32658         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32659         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32660         if(v1 < v2){
32661                         return dsc ? +1 : -1;
32662                 }else if(v1 > v2){
32663                         return dsc ? -1 : +1;
32664         }else{
32665                 return 0;
32666         }
32667     };
32668 };
32669
32670 Roo.tree.TreeSorter.prototype = {
32671     doSort : function(node){
32672         node.sort(this.sortFn);
32673     },
32674     
32675     compareNodes : function(n1, n2){
32676         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32677     },
32678     
32679     updateSort : function(tree, node){
32680         if(node.childrenRendered){
32681             this.doSort.defer(1, this, [node]);
32682         }
32683     }
32684 };/*
32685  * Based on:
32686  * Ext JS Library 1.1.1
32687  * Copyright(c) 2006-2007, Ext JS, LLC.
32688  *
32689  * Originally Released Under LGPL - original licence link has changed is not relivant.
32690  *
32691  * Fork - LGPL
32692  * <script type="text/javascript">
32693  */
32694
32695 if(Roo.dd.DropZone){
32696     
32697 Roo.tree.TreeDropZone = function(tree, config){
32698     this.allowParentInsert = false;
32699     this.allowContainerDrop = false;
32700     this.appendOnly = false;
32701     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32702     this.tree = tree;
32703     this.lastInsertClass = "x-tree-no-status";
32704     this.dragOverData = {};
32705 };
32706
32707 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32708     ddGroup : "TreeDD",
32709     
32710     expandDelay : 1000,
32711     
32712     expandNode : function(node){
32713         if(node.hasChildNodes() && !node.isExpanded()){
32714             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32715         }
32716     },
32717     
32718     queueExpand : function(node){
32719         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32720     },
32721     
32722     cancelExpand : function(){
32723         if(this.expandProcId){
32724             clearTimeout(this.expandProcId);
32725             this.expandProcId = false;
32726         }
32727     },
32728     
32729     isValidDropPoint : function(n, pt, dd, e, data){
32730         if(!n || !data){ return false; }
32731         var targetNode = n.node;
32732         var dropNode = data.node;
32733         // default drop rules
32734         if(!(targetNode && targetNode.isTarget && pt)){
32735             return false;
32736         }
32737         if(pt == "append" && targetNode.allowChildren === false){
32738             return false;
32739         }
32740         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32741             return false;
32742         }
32743         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32744             return false;
32745         }
32746         // reuse the object
32747         var overEvent = this.dragOverData;
32748         overEvent.tree = this.tree;
32749         overEvent.target = targetNode;
32750         overEvent.data = data;
32751         overEvent.point = pt;
32752         overEvent.source = dd;
32753         overEvent.rawEvent = e;
32754         overEvent.dropNode = dropNode;
32755         overEvent.cancel = false;  
32756         var result = this.tree.fireEvent("nodedragover", overEvent);
32757         return overEvent.cancel === false && result !== false;
32758     },
32759     
32760     getDropPoint : function(e, n, dd){
32761         var tn = n.node;
32762         if(tn.isRoot){
32763             return tn.allowChildren !== false ? "append" : false; // always append for root
32764         }
32765         var dragEl = n.ddel;
32766         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32767         var y = Roo.lib.Event.getPageY(e);
32768         var noAppend = tn.allowChildren === false || tn.isLeaf();
32769         if(this.appendOnly || tn.parentNode.allowChildren === false){
32770             return noAppend ? false : "append";
32771         }
32772         var noBelow = false;
32773         if(!this.allowParentInsert){
32774             noBelow = tn.hasChildNodes() && tn.isExpanded();
32775         }
32776         var q = (b - t) / (noAppend ? 2 : 3);
32777         if(y >= t && y < (t + q)){
32778             return "above";
32779         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32780             return "below";
32781         }else{
32782             return "append";
32783         }
32784     },
32785     
32786     onNodeEnter : function(n, dd, e, data){
32787         this.cancelExpand();
32788     },
32789     
32790     onNodeOver : function(n, dd, e, data){
32791         var pt = this.getDropPoint(e, n, dd);
32792         var node = n.node;
32793         
32794         // auto node expand check
32795         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32796             this.queueExpand(node);
32797         }else if(pt != "append"){
32798             this.cancelExpand();
32799         }
32800         
32801         // set the insert point style on the target node
32802         var returnCls = this.dropNotAllowed;
32803         if(this.isValidDropPoint(n, pt, dd, e, data)){
32804            if(pt){
32805                var el = n.ddel;
32806                var cls;
32807                if(pt == "above"){
32808                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32809                    cls = "x-tree-drag-insert-above";
32810                }else if(pt == "below"){
32811                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32812                    cls = "x-tree-drag-insert-below";
32813                }else{
32814                    returnCls = "x-tree-drop-ok-append";
32815                    cls = "x-tree-drag-append";
32816                }
32817                if(this.lastInsertClass != cls){
32818                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32819                    this.lastInsertClass = cls;
32820                }
32821            }
32822        }
32823        return returnCls;
32824     },
32825     
32826     onNodeOut : function(n, dd, e, data){
32827         this.cancelExpand();
32828         this.removeDropIndicators(n);
32829     },
32830     
32831     onNodeDrop : function(n, dd, e, data){
32832         var point = this.getDropPoint(e, n, dd);
32833         var targetNode = n.node;
32834         targetNode.ui.startDrop();
32835         if(!this.isValidDropPoint(n, point, dd, e, data)){
32836             targetNode.ui.endDrop();
32837             return false;
32838         }
32839         // first try to find the drop node
32840         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32841         var dropEvent = {
32842             tree : this.tree,
32843             target: targetNode,
32844             data: data,
32845             point: point,
32846             source: dd,
32847             rawEvent: e,
32848             dropNode: dropNode,
32849             cancel: !dropNode   
32850         };
32851         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32852         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32853             targetNode.ui.endDrop();
32854             return false;
32855         }
32856         // allow target changing
32857         targetNode = dropEvent.target;
32858         if(point == "append" && !targetNode.isExpanded()){
32859             targetNode.expand(false, null, function(){
32860                 this.completeDrop(dropEvent);
32861             }.createDelegate(this));
32862         }else{
32863             this.completeDrop(dropEvent);
32864         }
32865         return true;
32866     },
32867     
32868     completeDrop : function(de){
32869         var ns = de.dropNode, p = de.point, t = de.target;
32870         if(!(ns instanceof Array)){
32871             ns = [ns];
32872         }
32873         var n;
32874         for(var i = 0, len = ns.length; i < len; i++){
32875             n = ns[i];
32876             if(p == "above"){
32877                 t.parentNode.insertBefore(n, t);
32878             }else if(p == "below"){
32879                 t.parentNode.insertBefore(n, t.nextSibling);
32880             }else{
32881                 t.appendChild(n);
32882             }
32883         }
32884         n.ui.focus();
32885         if(this.tree.hlDrop){
32886             n.ui.highlight();
32887         }
32888         t.ui.endDrop();
32889         this.tree.fireEvent("nodedrop", de);
32890     },
32891     
32892     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32893         if(this.tree.hlDrop){
32894             dropNode.ui.focus();
32895             dropNode.ui.highlight();
32896         }
32897         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32898     },
32899     
32900     getTree : function(){
32901         return this.tree;
32902     },
32903     
32904     removeDropIndicators : function(n){
32905         if(n && n.ddel){
32906             var el = n.ddel;
32907             Roo.fly(el).removeClass([
32908                     "x-tree-drag-insert-above",
32909                     "x-tree-drag-insert-below",
32910                     "x-tree-drag-append"]);
32911             this.lastInsertClass = "_noclass";
32912         }
32913     },
32914     
32915     beforeDragDrop : function(target, e, id){
32916         this.cancelExpand();
32917         return true;
32918     },
32919     
32920     afterRepair : function(data){
32921         if(data && Roo.enableFx){
32922             data.node.ui.highlight();
32923         }
32924         this.hideProxy();
32925     }    
32926 });
32927
32928 }/*
32929  * Based on:
32930  * Ext JS Library 1.1.1
32931  * Copyright(c) 2006-2007, Ext JS, LLC.
32932  *
32933  * Originally Released Under LGPL - original licence link has changed is not relivant.
32934  *
32935  * Fork - LGPL
32936  * <script type="text/javascript">
32937  */
32938  
32939
32940 if(Roo.dd.DragZone){
32941 Roo.tree.TreeDragZone = function(tree, config){
32942     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
32943     this.tree = tree;
32944 };
32945
32946 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
32947     ddGroup : "TreeDD",
32948     
32949     onBeforeDrag : function(data, e){
32950         var n = data.node;
32951         return n && n.draggable && !n.disabled;
32952     },
32953     
32954     onInitDrag : function(e){
32955         var data = this.dragData;
32956         this.tree.getSelectionModel().select(data.node);
32957         this.proxy.update("");
32958         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
32959         this.tree.fireEvent("startdrag", this.tree, data.node, e);
32960     },
32961     
32962     getRepairXY : function(e, data){
32963         return data.node.ui.getDDRepairXY();
32964     },
32965     
32966     onEndDrag : function(data, e){
32967         this.tree.fireEvent("enddrag", this.tree, data.node, e);
32968     },
32969     
32970     onValidDrop : function(dd, e, id){
32971         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
32972         this.hideProxy();
32973     },
32974     
32975     beforeInvalidDrop : function(e, id){
32976         // this scrolls the original position back into view
32977         var sm = this.tree.getSelectionModel();
32978         sm.clearSelections();
32979         sm.select(this.dragData.node);
32980     }
32981 });
32982 }/*
32983  * Based on:
32984  * Ext JS Library 1.1.1
32985  * Copyright(c) 2006-2007, Ext JS, LLC.
32986  *
32987  * Originally Released Under LGPL - original licence link has changed is not relivant.
32988  *
32989  * Fork - LGPL
32990  * <script type="text/javascript">
32991  */
32992 /**
32993  * @class Roo.tree.TreeEditor
32994  * @extends Roo.Editor
32995  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
32996  * as the editor field.
32997  * @constructor
32998  * @param {TreePanel} tree
32999  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33000  */
33001 Roo.tree.TreeEditor = function(tree, config){
33002     config = config || {};
33003     var field = config.events ? config : new Roo.form.TextField(config);
33004     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33005
33006     this.tree = tree;
33007
33008     tree.on('beforeclick', this.beforeNodeClick, this);
33009     tree.getTreeEl().on('mousedown', this.hide, this);
33010     this.on('complete', this.updateNode, this);
33011     this.on('beforestartedit', this.fitToTree, this);
33012     this.on('startedit', this.bindScroll, this, {delay:10});
33013     this.on('specialkey', this.onSpecialKey, this);
33014 };
33015
33016 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33017     /**
33018      * @cfg {String} alignment
33019      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33020      */
33021     alignment: "l-l",
33022     // inherit
33023     autoSize: false,
33024     /**
33025      * @cfg {Boolean} hideEl
33026      * True to hide the bound element while the editor is displayed (defaults to false)
33027      */
33028     hideEl : false,
33029     /**
33030      * @cfg {String} cls
33031      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33032      */
33033     cls: "x-small-editor x-tree-editor",
33034     /**
33035      * @cfg {Boolean} shim
33036      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33037      */
33038     shim:false,
33039     // inherit
33040     shadow:"frame",
33041     /**
33042      * @cfg {Number} maxWidth
33043      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33044      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33045      * scroll and client offsets into account prior to each edit.
33046      */
33047     maxWidth: 250,
33048
33049     editDelay : 350,
33050
33051     // private
33052     fitToTree : function(ed, el){
33053         var td = this.tree.getTreeEl().dom, nd = el.dom;
33054         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33055             td.scrollLeft = nd.offsetLeft;
33056         }
33057         var w = Math.min(
33058                 this.maxWidth,
33059                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33060         this.setSize(w, '');
33061     },
33062
33063     // private
33064     triggerEdit : function(node){
33065         this.completeEdit();
33066         this.editNode = node;
33067         this.startEdit(node.ui.textNode, node.text);
33068     },
33069
33070     // private
33071     bindScroll : function(){
33072         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33073     },
33074
33075     // private
33076     beforeNodeClick : function(node, e){
33077         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33078         this.lastClick = new Date();
33079         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33080             e.stopEvent();
33081             this.triggerEdit(node);
33082             return false;
33083         }
33084     },
33085
33086     // private
33087     updateNode : function(ed, value){
33088         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33089         this.editNode.setText(value);
33090     },
33091
33092     // private
33093     onHide : function(){
33094         Roo.tree.TreeEditor.superclass.onHide.call(this);
33095         if(this.editNode){
33096             this.editNode.ui.focus();
33097         }
33098     },
33099
33100     // private
33101     onSpecialKey : function(field, e){
33102         var k = e.getKey();
33103         if(k == e.ESC){
33104             e.stopEvent();
33105             this.cancelEdit();
33106         }else if(k == e.ENTER && !e.hasModifier()){
33107             e.stopEvent();
33108             this.completeEdit();
33109         }
33110     }
33111 });//<Script type="text/javascript">
33112 /*
33113  * Based on:
33114  * Ext JS Library 1.1.1
33115  * Copyright(c) 2006-2007, Ext JS, LLC.
33116  *
33117  * Originally Released Under LGPL - original licence link has changed is not relivant.
33118  *
33119  * Fork - LGPL
33120  * <script type="text/javascript">
33121  */
33122  
33123 /**
33124  * Not documented??? - probably should be...
33125  */
33126
33127 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33128     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33129     
33130     renderElements : function(n, a, targetNode, bulkRender){
33131         //consel.log("renderElements?");
33132         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33133
33134         var t = n.getOwnerTree();
33135         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33136         
33137         var cols = t.columns;
33138         var bw = t.borderWidth;
33139         var c = cols[0];
33140         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33141          var cb = typeof a.checked == "boolean";
33142         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33143         var colcls = 'x-t-' + tid + '-c0';
33144         var buf = [
33145             '<li class="x-tree-node">',
33146             
33147                 
33148                 '<div class="x-tree-node-el ', a.cls,'">',
33149                     // extran...
33150                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33151                 
33152                 
33153                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33154                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33155                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33156                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33157                            (a.iconCls ? ' '+a.iconCls : ''),
33158                            '" unselectable="on" />',
33159                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33160                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33161                              
33162                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33163                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33164                             '<span unselectable="on" qtip="' + tx + '">',
33165                              tx,
33166                              '</span></a>' ,
33167                     '</div>',
33168                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33169                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33170                  ];
33171         
33172         for(var i = 1, len = cols.length; i < len; i++){
33173             c = cols[i];
33174             colcls = 'x-t-' + tid + '-c' +i;
33175             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33176             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33177                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33178                       "</div>");
33179          }
33180          
33181          buf.push(
33182             '</a>',
33183             '<div class="x-clear"></div></div>',
33184             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33185             "</li>");
33186         
33187         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33188             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33189                                 n.nextSibling.ui.getEl(), buf.join(""));
33190         }else{
33191             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33192         }
33193         var el = this.wrap.firstChild;
33194         this.elRow = el;
33195         this.elNode = el.firstChild;
33196         this.ranchor = el.childNodes[1];
33197         this.ctNode = this.wrap.childNodes[1];
33198         var cs = el.firstChild.childNodes;
33199         this.indentNode = cs[0];
33200         this.ecNode = cs[1];
33201         this.iconNode = cs[2];
33202         var index = 3;
33203         if(cb){
33204             this.checkbox = cs[3];
33205             index++;
33206         }
33207         this.anchor = cs[index];
33208         
33209         this.textNode = cs[index].firstChild;
33210         
33211         //el.on("click", this.onClick, this);
33212         //el.on("dblclick", this.onDblClick, this);
33213         
33214         
33215        // console.log(this);
33216     },
33217     initEvents : function(){
33218         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33219         
33220             
33221         var a = this.ranchor;
33222
33223         var el = Roo.get(a);
33224
33225         if(Roo.isOpera){ // opera render bug ignores the CSS
33226             el.setStyle("text-decoration", "none");
33227         }
33228
33229         el.on("click", this.onClick, this);
33230         el.on("dblclick", this.onDblClick, this);
33231         el.on("contextmenu", this.onContextMenu, this);
33232         
33233     },
33234     
33235     /*onSelectedChange : function(state){
33236         if(state){
33237             this.focus();
33238             this.addClass("x-tree-selected");
33239         }else{
33240             //this.blur();
33241             this.removeClass("x-tree-selected");
33242         }
33243     },*/
33244     addClass : function(cls){
33245         if(this.elRow){
33246             Roo.fly(this.elRow).addClass(cls);
33247         }
33248         
33249     },
33250     
33251     
33252     removeClass : function(cls){
33253         if(this.elRow){
33254             Roo.fly(this.elRow).removeClass(cls);
33255         }
33256     }
33257
33258     
33259     
33260 });//<Script type="text/javascript">
33261
33262 /*
33263  * Based on:
33264  * Ext JS Library 1.1.1
33265  * Copyright(c) 2006-2007, Ext JS, LLC.
33266  *
33267  * Originally Released Under LGPL - original licence link has changed is not relivant.
33268  *
33269  * Fork - LGPL
33270  * <script type="text/javascript">
33271  */
33272  
33273
33274 /**
33275  * @class Roo.tree.ColumnTree
33276  * @extends Roo.data.TreePanel
33277  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33278  * @cfg {int} borderWidth  compined right/left border allowance
33279  * @constructor
33280  * @param {String/HTMLElement/Element} el The container element
33281  * @param {Object} config
33282  */
33283 Roo.tree.ColumnTree =  function(el, config)
33284 {
33285    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33286    this.addEvents({
33287         /**
33288         * @event resize
33289         * Fire this event on a container when it resizes
33290         * @param {int} w Width
33291         * @param {int} h Height
33292         */
33293        "resize" : true
33294     });
33295     this.on('resize', this.onResize, this);
33296 };
33297
33298 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33299     //lines:false,
33300     
33301     
33302     borderWidth: Roo.isBorderBox ? 0 : 2, 
33303     headEls : false,
33304     
33305     render : function(){
33306         // add the header.....
33307        
33308         Roo.tree.ColumnTree.superclass.render.apply(this);
33309         
33310         this.el.addClass('x-column-tree');
33311         
33312         this.headers = this.el.createChild(
33313             {cls:'x-tree-headers'},this.innerCt.dom);
33314    
33315         var cols = this.columns, c;
33316         var totalWidth = 0;
33317         this.headEls = [];
33318         var  len = cols.length;
33319         for(var i = 0; i < len; i++){
33320              c = cols[i];
33321              totalWidth += c.width;
33322             this.headEls.push(this.headers.createChild({
33323                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33324                  cn: {
33325                      cls:'x-tree-hd-text',
33326                      html: c.header
33327                  },
33328                  style:'width:'+(c.width-this.borderWidth)+'px;'
33329              }));
33330         }
33331         this.headers.createChild({cls:'x-clear'});
33332         // prevent floats from wrapping when clipped
33333         this.headers.setWidth(totalWidth);
33334         //this.innerCt.setWidth(totalWidth);
33335         this.innerCt.setStyle({ overflow: 'auto' });
33336         this.onResize(this.width, this.height);
33337              
33338         
33339     },
33340     onResize : function(w,h)
33341     {
33342         this.height = h;
33343         this.width = w;
33344         // resize cols..
33345         this.innerCt.setWidth(this.width);
33346         this.innerCt.setHeight(this.height-20);
33347         
33348         // headers...
33349         var cols = this.columns, c;
33350         var totalWidth = 0;
33351         var expEl = false;
33352         var len = cols.length;
33353         for(var i = 0; i < len; i++){
33354             c = cols[i];
33355             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33356                 // it's the expander..
33357                 expEl  = this.headEls[i];
33358                 continue;
33359             }
33360             totalWidth += c.width;
33361             
33362         }
33363         if (expEl) {
33364             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33365         }
33366         this.headers.setWidth(w-20);
33367
33368         
33369         
33370         
33371     }
33372 });
33373 /*
33374  * Based on:
33375  * Ext JS Library 1.1.1
33376  * Copyright(c) 2006-2007, Ext JS, LLC.
33377  *
33378  * Originally Released Under LGPL - original licence link has changed is not relivant.
33379  *
33380  * Fork - LGPL
33381  * <script type="text/javascript">
33382  */
33383  
33384 /**
33385  * @class Roo.menu.Menu
33386  * @extends Roo.util.Observable
33387  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33388  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33389  * @constructor
33390  * Creates a new Menu
33391  * @param {Object} config Configuration options
33392  */
33393 Roo.menu.Menu = function(config){
33394     Roo.apply(this, config);
33395     this.id = this.id || Roo.id();
33396     this.addEvents({
33397         /**
33398          * @event beforeshow
33399          * Fires before this menu is displayed
33400          * @param {Roo.menu.Menu} this
33401          */
33402         beforeshow : true,
33403         /**
33404          * @event beforehide
33405          * Fires before this menu is hidden
33406          * @param {Roo.menu.Menu} this
33407          */
33408         beforehide : true,
33409         /**
33410          * @event show
33411          * Fires after this menu is displayed
33412          * @param {Roo.menu.Menu} this
33413          */
33414         show : true,
33415         /**
33416          * @event hide
33417          * Fires after this menu is hidden
33418          * @param {Roo.menu.Menu} this
33419          */
33420         hide : true,
33421         /**
33422          * @event click
33423          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33424          * @param {Roo.menu.Menu} this
33425          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33426          * @param {Roo.EventObject} e
33427          */
33428         click : true,
33429         /**
33430          * @event mouseover
33431          * Fires when the mouse is hovering over this menu
33432          * @param {Roo.menu.Menu} this
33433          * @param {Roo.EventObject} e
33434          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33435          */
33436         mouseover : true,
33437         /**
33438          * @event mouseout
33439          * Fires when the mouse exits this menu
33440          * @param {Roo.menu.Menu} this
33441          * @param {Roo.EventObject} e
33442          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33443          */
33444         mouseout : true,
33445         /**
33446          * @event itemclick
33447          * Fires when a menu item contained in this menu is clicked
33448          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33449          * @param {Roo.EventObject} e
33450          */
33451         itemclick: true
33452     });
33453     if (this.registerMenu) {
33454         Roo.menu.MenuMgr.register(this);
33455     }
33456     
33457     var mis = this.items;
33458     this.items = new Roo.util.MixedCollection();
33459     if(mis){
33460         this.add.apply(this, mis);
33461     }
33462 };
33463
33464 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33465     /**
33466      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33467      */
33468     minWidth : 120,
33469     /**
33470      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33471      * for bottom-right shadow (defaults to "sides")
33472      */
33473     shadow : "sides",
33474     /**
33475      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33476      * this menu (defaults to "tl-tr?")
33477      */
33478     subMenuAlign : "tl-tr?",
33479     /**
33480      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33481      * relative to its element of origin (defaults to "tl-bl?")
33482      */
33483     defaultAlign : "tl-bl?",
33484     /**
33485      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33486      */
33487     allowOtherMenus : false,
33488     /**
33489      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33490      */
33491     registerMenu : true,
33492
33493     hidden:true,
33494
33495     // private
33496     render : function(){
33497         if(this.el){
33498             return;
33499         }
33500         var el = this.el = new Roo.Layer({
33501             cls: "x-menu",
33502             shadow:this.shadow,
33503             constrain: false,
33504             parentEl: this.parentEl || document.body,
33505             zindex:15000
33506         });
33507
33508         this.keyNav = new Roo.menu.MenuNav(this);
33509
33510         if(this.plain){
33511             el.addClass("x-menu-plain");
33512         }
33513         if(this.cls){
33514             el.addClass(this.cls);
33515         }
33516         // generic focus element
33517         this.focusEl = el.createChild({
33518             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33519         });
33520         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33521         ul.on("click", this.onClick, this);
33522         ul.on("mouseover", this.onMouseOver, this);
33523         ul.on("mouseout", this.onMouseOut, this);
33524         this.items.each(function(item){
33525             var li = document.createElement("li");
33526             li.className = "x-menu-list-item";
33527             ul.dom.appendChild(li);
33528             item.render(li, this);
33529         }, this);
33530         this.ul = ul;
33531         this.autoWidth();
33532     },
33533
33534     // private
33535     autoWidth : function(){
33536         var el = this.el, ul = this.ul;
33537         if(!el){
33538             return;
33539         }
33540         var w = this.width;
33541         if(w){
33542             el.setWidth(w);
33543         }else if(Roo.isIE){
33544             el.setWidth(this.minWidth);
33545             var t = el.dom.offsetWidth; // force recalc
33546             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33547         }
33548     },
33549
33550     // private
33551     delayAutoWidth : function(){
33552         if(this.rendered){
33553             if(!this.awTask){
33554                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33555             }
33556             this.awTask.delay(20);
33557         }
33558     },
33559
33560     // private
33561     findTargetItem : function(e){
33562         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33563         if(t && t.menuItemId){
33564             return this.items.get(t.menuItemId);
33565         }
33566     },
33567
33568     // private
33569     onClick : function(e){
33570         var t;
33571         if(t = this.findTargetItem(e)){
33572             t.onClick(e);
33573             this.fireEvent("click", this, t, e);
33574         }
33575     },
33576
33577     // private
33578     setActiveItem : function(item, autoExpand){
33579         if(item != this.activeItem){
33580             if(this.activeItem){
33581                 this.activeItem.deactivate();
33582             }
33583             this.activeItem = item;
33584             item.activate(autoExpand);
33585         }else if(autoExpand){
33586             item.expandMenu();
33587         }
33588     },
33589
33590     // private
33591     tryActivate : function(start, step){
33592         var items = this.items;
33593         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33594             var item = items.get(i);
33595             if(!item.disabled && item.canActivate){
33596                 this.setActiveItem(item, false);
33597                 return item;
33598             }
33599         }
33600         return false;
33601     },
33602
33603     // private
33604     onMouseOver : function(e){
33605         var t;
33606         if(t = this.findTargetItem(e)){
33607             if(t.canActivate && !t.disabled){
33608                 this.setActiveItem(t, true);
33609             }
33610         }
33611         this.fireEvent("mouseover", this, e, t);
33612     },
33613
33614     // private
33615     onMouseOut : function(e){
33616         var t;
33617         if(t = this.findTargetItem(e)){
33618             if(t == this.activeItem && t.shouldDeactivate(e)){
33619                 this.activeItem.deactivate();
33620                 delete this.activeItem;
33621             }
33622         }
33623         this.fireEvent("mouseout", this, e, t);
33624     },
33625
33626     /**
33627      * Read-only.  Returns true if the menu is currently displayed, else false.
33628      * @type Boolean
33629      */
33630     isVisible : function(){
33631         return this.el && !this.hidden;
33632     },
33633
33634     /**
33635      * Displays this menu relative to another element
33636      * @param {String/HTMLElement/Roo.Element} element The element to align to
33637      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33638      * the element (defaults to this.defaultAlign)
33639      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33640      */
33641     show : function(el, pos, parentMenu){
33642         this.parentMenu = parentMenu;
33643         if(!this.el){
33644             this.render();
33645         }
33646         this.fireEvent("beforeshow", this);
33647         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33648     },
33649
33650     /**
33651      * Displays this menu at a specific xy position
33652      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33653      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33654      */
33655     showAt : function(xy, parentMenu, /* private: */_e){
33656         this.parentMenu = parentMenu;
33657         if(!this.el){
33658             this.render();
33659         }
33660         if(_e !== false){
33661             this.fireEvent("beforeshow", this);
33662             xy = this.el.adjustForConstraints(xy);
33663         }
33664         this.el.setXY(xy);
33665         this.el.show();
33666         this.hidden = false;
33667         this.focus();
33668         this.fireEvent("show", this);
33669     },
33670
33671     focus : function(){
33672         if(!this.hidden){
33673             this.doFocus.defer(50, this);
33674         }
33675     },
33676
33677     doFocus : function(){
33678         if(!this.hidden){
33679             this.focusEl.focus();
33680         }
33681     },
33682
33683     /**
33684      * Hides this menu and optionally all parent menus
33685      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33686      */
33687     hide : function(deep){
33688         if(this.el && this.isVisible()){
33689             this.fireEvent("beforehide", this);
33690             if(this.activeItem){
33691                 this.activeItem.deactivate();
33692                 this.activeItem = null;
33693             }
33694             this.el.hide();
33695             this.hidden = true;
33696             this.fireEvent("hide", this);
33697         }
33698         if(deep === true && this.parentMenu){
33699             this.parentMenu.hide(true);
33700         }
33701     },
33702
33703     /**
33704      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33705      * Any of the following are valid:
33706      * <ul>
33707      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33708      * <li>An HTMLElement object which will be converted to a menu item</li>
33709      * <li>A menu item config object that will be created as a new menu item</li>
33710      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33711      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33712      * </ul>
33713      * Usage:
33714      * <pre><code>
33715 // Create the menu
33716 var menu = new Roo.menu.Menu();
33717
33718 // Create a menu item to add by reference
33719 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33720
33721 // Add a bunch of items at once using different methods.
33722 // Only the last item added will be returned.
33723 var item = menu.add(
33724     menuItem,                // add existing item by ref
33725     'Dynamic Item',          // new TextItem
33726     '-',                     // new separator
33727     { text: 'Config Item' }  // new item by config
33728 );
33729 </code></pre>
33730      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33731      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33732      */
33733     add : function(){
33734         var a = arguments, l = a.length, item;
33735         for(var i = 0; i < l; i++){
33736             var el = a[i];
33737             if(el.render){ // some kind of Item
33738                 item = this.addItem(el);
33739             }else if(typeof el == "string"){ // string
33740                 if(el == "separator" || el == "-"){
33741                     item = this.addSeparator();
33742                 }else{
33743                     item = this.addText(el);
33744                 }
33745             }else if(el.tagName || el.el){ // element
33746                 item = this.addElement(el);
33747             }else if(typeof el == "object"){ // must be menu item config?
33748                 item = this.addMenuItem(el);
33749             }
33750         }
33751         return item;
33752     },
33753
33754     /**
33755      * Returns this menu's underlying {@link Roo.Element} object
33756      * @return {Roo.Element} The element
33757      */
33758     getEl : function(){
33759         if(!this.el){
33760             this.render();
33761         }
33762         return this.el;
33763     },
33764
33765     /**
33766      * Adds a separator bar to the menu
33767      * @return {Roo.menu.Item} The menu item that was added
33768      */
33769     addSeparator : function(){
33770         return this.addItem(new Roo.menu.Separator());
33771     },
33772
33773     /**
33774      * Adds an {@link Roo.Element} object to the menu
33775      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33776      * @return {Roo.menu.Item} The menu item that was added
33777      */
33778     addElement : function(el){
33779         return this.addItem(new Roo.menu.BaseItem(el));
33780     },
33781
33782     /**
33783      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33784      * @param {Roo.menu.Item} item The menu item to add
33785      * @return {Roo.menu.Item} The menu item that was added
33786      */
33787     addItem : function(item){
33788         this.items.add(item);
33789         if(this.ul){
33790             var li = document.createElement("li");
33791             li.className = "x-menu-list-item";
33792             this.ul.dom.appendChild(li);
33793             item.render(li, this);
33794             this.delayAutoWidth();
33795         }
33796         return item;
33797     },
33798
33799     /**
33800      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33801      * @param {Object} config A MenuItem config object
33802      * @return {Roo.menu.Item} The menu item that was added
33803      */
33804     addMenuItem : function(config){
33805         if(!(config instanceof Roo.menu.Item)){
33806             if(typeof config.checked == "boolean"){ // must be check menu item config?
33807                 config = new Roo.menu.CheckItem(config);
33808             }else{
33809                 config = new Roo.menu.Item(config);
33810             }
33811         }
33812         return this.addItem(config);
33813     },
33814
33815     /**
33816      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33817      * @param {String} text The text to display in the menu item
33818      * @return {Roo.menu.Item} The menu item that was added
33819      */
33820     addText : function(text){
33821         return this.addItem(new Roo.menu.TextItem(text));
33822     },
33823
33824     /**
33825      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33826      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33827      * @param {Roo.menu.Item} item The menu item to add
33828      * @return {Roo.menu.Item} The menu item that was added
33829      */
33830     insert : function(index, item){
33831         this.items.insert(index, item);
33832         if(this.ul){
33833             var li = document.createElement("li");
33834             li.className = "x-menu-list-item";
33835             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33836             item.render(li, this);
33837             this.delayAutoWidth();
33838         }
33839         return item;
33840     },
33841
33842     /**
33843      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33844      * @param {Roo.menu.Item} item The menu item to remove
33845      */
33846     remove : function(item){
33847         this.items.removeKey(item.id);
33848         item.destroy();
33849     },
33850
33851     /**
33852      * Removes and destroys all items in the menu
33853      */
33854     removeAll : function(){
33855         var f;
33856         while(f = this.items.first()){
33857             this.remove(f);
33858         }
33859     }
33860 });
33861
33862 // MenuNav is a private utility class used internally by the Menu
33863 Roo.menu.MenuNav = function(menu){
33864     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33865     this.scope = this.menu = menu;
33866 };
33867
33868 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33869     doRelay : function(e, h){
33870         var k = e.getKey();
33871         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33872             this.menu.tryActivate(0, 1);
33873             return false;
33874         }
33875         return h.call(this.scope || this, e, this.menu);
33876     },
33877
33878     up : function(e, m){
33879         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33880             m.tryActivate(m.items.length-1, -1);
33881         }
33882     },
33883
33884     down : function(e, m){
33885         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33886             m.tryActivate(0, 1);
33887         }
33888     },
33889
33890     right : function(e, m){
33891         if(m.activeItem){
33892             m.activeItem.expandMenu(true);
33893         }
33894     },
33895
33896     left : function(e, m){
33897         m.hide();
33898         if(m.parentMenu && m.parentMenu.activeItem){
33899             m.parentMenu.activeItem.activate();
33900         }
33901     },
33902
33903     enter : function(e, m){
33904         if(m.activeItem){
33905             e.stopPropagation();
33906             m.activeItem.onClick(e);
33907             m.fireEvent("click", this, m.activeItem);
33908             return true;
33909         }
33910     }
33911 });/*
33912  * Based on:
33913  * Ext JS Library 1.1.1
33914  * Copyright(c) 2006-2007, Ext JS, LLC.
33915  *
33916  * Originally Released Under LGPL - original licence link has changed is not relivant.
33917  *
33918  * Fork - LGPL
33919  * <script type="text/javascript">
33920  */
33921  
33922 /**
33923  * @class Roo.menu.MenuMgr
33924  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33925  * @singleton
33926  */
33927 Roo.menu.MenuMgr = function(){
33928    var menus, active, groups = {}, attached = false, lastShow = new Date();
33929
33930    // private - called when first menu is created
33931    function init(){
33932        menus = {};
33933        active = new Roo.util.MixedCollection();
33934        Roo.get(document).addKeyListener(27, function(){
33935            if(active.length > 0){
33936                hideAll();
33937            }
33938        });
33939    }
33940
33941    // private
33942    function hideAll(){
33943        if(active && active.length > 0){
33944            var c = active.clone();
33945            c.each(function(m){
33946                m.hide();
33947            });
33948        }
33949    }
33950
33951    // private
33952    function onHide(m){
33953        active.remove(m);
33954        if(active.length < 1){
33955            Roo.get(document).un("mousedown", onMouseDown);
33956            attached = false;
33957        }
33958    }
33959
33960    // private
33961    function onShow(m){
33962        var last = active.last();
33963        lastShow = new Date();
33964        active.add(m);
33965        if(!attached){
33966            Roo.get(document).on("mousedown", onMouseDown);
33967            attached = true;
33968        }
33969        if(m.parentMenu){
33970           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
33971           m.parentMenu.activeChild = m;
33972        }else if(last && last.isVisible()){
33973           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
33974        }
33975    }
33976
33977    // private
33978    function onBeforeHide(m){
33979        if(m.activeChild){
33980            m.activeChild.hide();
33981        }
33982        if(m.autoHideTimer){
33983            clearTimeout(m.autoHideTimer);
33984            delete m.autoHideTimer;
33985        }
33986    }
33987
33988    // private
33989    function onBeforeShow(m){
33990        var pm = m.parentMenu;
33991        if(!pm && !m.allowOtherMenus){
33992            hideAll();
33993        }else if(pm && pm.activeChild && active != m){
33994            pm.activeChild.hide();
33995        }
33996    }
33997
33998    // private
33999    function onMouseDown(e){
34000        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34001            hideAll();
34002        }
34003    }
34004
34005    // private
34006    function onBeforeCheck(mi, state){
34007        if(state){
34008            var g = groups[mi.group];
34009            for(var i = 0, l = g.length; i < l; i++){
34010                if(g[i] != mi){
34011                    g[i].setChecked(false);
34012                }
34013            }
34014        }
34015    }
34016
34017    return {
34018
34019        /**
34020         * Hides all menus that are currently visible
34021         */
34022        hideAll : function(){
34023             hideAll();  
34024        },
34025
34026        // private
34027        register : function(menu){
34028            if(!menus){
34029                init();
34030            }
34031            menus[menu.id] = menu;
34032            menu.on("beforehide", onBeforeHide);
34033            menu.on("hide", onHide);
34034            menu.on("beforeshow", onBeforeShow);
34035            menu.on("show", onShow);
34036            var g = menu.group;
34037            if(g && menu.events["checkchange"]){
34038                if(!groups[g]){
34039                    groups[g] = [];
34040                }
34041                groups[g].push(menu);
34042                menu.on("checkchange", onCheck);
34043            }
34044        },
34045
34046         /**
34047          * Returns a {@link Roo.menu.Menu} object
34048          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34049          * be used to generate and return a new Menu instance.
34050          */
34051        get : function(menu){
34052            if(typeof menu == "string"){ // menu id
34053                return menus[menu];
34054            }else if(menu.events){  // menu instance
34055                return menu;
34056            }else if(typeof menu.length == 'number'){ // array of menu items?
34057                return new Roo.menu.Menu({items:menu});
34058            }else{ // otherwise, must be a config
34059                return new Roo.menu.Menu(menu);
34060            }
34061        },
34062
34063        // private
34064        unregister : function(menu){
34065            delete menus[menu.id];
34066            menu.un("beforehide", onBeforeHide);
34067            menu.un("hide", onHide);
34068            menu.un("beforeshow", onBeforeShow);
34069            menu.un("show", onShow);
34070            var g = menu.group;
34071            if(g && menu.events["checkchange"]){
34072                groups[g].remove(menu);
34073                menu.un("checkchange", onCheck);
34074            }
34075        },
34076
34077        // private
34078        registerCheckable : function(menuItem){
34079            var g = menuItem.group;
34080            if(g){
34081                if(!groups[g]){
34082                    groups[g] = [];
34083                }
34084                groups[g].push(menuItem);
34085                menuItem.on("beforecheckchange", onBeforeCheck);
34086            }
34087        },
34088
34089        // private
34090        unregisterCheckable : function(menuItem){
34091            var g = menuItem.group;
34092            if(g){
34093                groups[g].remove(menuItem);
34094                menuItem.un("beforecheckchange", onBeforeCheck);
34095            }
34096        }
34097    };
34098 }();/*
34099  * Based on:
34100  * Ext JS Library 1.1.1
34101  * Copyright(c) 2006-2007, Ext JS, LLC.
34102  *
34103  * Originally Released Under LGPL - original licence link has changed is not relivant.
34104  *
34105  * Fork - LGPL
34106  * <script type="text/javascript">
34107  */
34108  
34109
34110 /**
34111  * @class Roo.menu.BaseItem
34112  * @extends Roo.Component
34113  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34114  * management and base configuration options shared by all menu components.
34115  * @constructor
34116  * Creates a new BaseItem
34117  * @param {Object} config Configuration options
34118  */
34119 Roo.menu.BaseItem = function(config){
34120     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34121
34122     this.addEvents({
34123         /**
34124          * @event click
34125          * Fires when this item is clicked
34126          * @param {Roo.menu.BaseItem} this
34127          * @param {Roo.EventObject} e
34128          */
34129         click: true,
34130         /**
34131          * @event activate
34132          * Fires when this item is activated
34133          * @param {Roo.menu.BaseItem} this
34134          */
34135         activate : true,
34136         /**
34137          * @event deactivate
34138          * Fires when this item is deactivated
34139          * @param {Roo.menu.BaseItem} this
34140          */
34141         deactivate : true
34142     });
34143
34144     if(this.handler){
34145         this.on("click", this.handler, this.scope, true);
34146     }
34147 };
34148
34149 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34150     /**
34151      * @cfg {Function} handler
34152      * A function that will handle the click event of this menu item (defaults to undefined)
34153      */
34154     /**
34155      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34156      */
34157     canActivate : false,
34158     /**
34159      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34160      */
34161     activeClass : "x-menu-item-active",
34162     /**
34163      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34164      */
34165     hideOnClick : true,
34166     /**
34167      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34168      */
34169     hideDelay : 100,
34170
34171     // private
34172     ctype: "Roo.menu.BaseItem",
34173
34174     // private
34175     actionMode : "container",
34176
34177     // private
34178     render : function(container, parentMenu){
34179         this.parentMenu = parentMenu;
34180         Roo.menu.BaseItem.superclass.render.call(this, container);
34181         this.container.menuItemId = this.id;
34182     },
34183
34184     // private
34185     onRender : function(container, position){
34186         this.el = Roo.get(this.el);
34187         container.dom.appendChild(this.el.dom);
34188     },
34189
34190     // private
34191     onClick : function(e){
34192         if(!this.disabled && this.fireEvent("click", this, e) !== false
34193                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34194             this.handleClick(e);
34195         }else{
34196             e.stopEvent();
34197         }
34198     },
34199
34200     // private
34201     activate : function(){
34202         if(this.disabled){
34203             return false;
34204         }
34205         var li = this.container;
34206         li.addClass(this.activeClass);
34207         this.region = li.getRegion().adjust(2, 2, -2, -2);
34208         this.fireEvent("activate", this);
34209         return true;
34210     },
34211
34212     // private
34213     deactivate : function(){
34214         this.container.removeClass(this.activeClass);
34215         this.fireEvent("deactivate", this);
34216     },
34217
34218     // private
34219     shouldDeactivate : function(e){
34220         return !this.region || !this.region.contains(e.getPoint());
34221     },
34222
34223     // private
34224     handleClick : function(e){
34225         if(this.hideOnClick){
34226             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34227         }
34228     },
34229
34230     // private
34231     expandMenu : function(autoActivate){
34232         // do nothing
34233     },
34234
34235     // private
34236     hideMenu : function(){
34237         // do nothing
34238     }
34239 });/*
34240  * Based on:
34241  * Ext JS Library 1.1.1
34242  * Copyright(c) 2006-2007, Ext JS, LLC.
34243  *
34244  * Originally Released Under LGPL - original licence link has changed is not relivant.
34245  *
34246  * Fork - LGPL
34247  * <script type="text/javascript">
34248  */
34249  
34250 /**
34251  * @class Roo.menu.Adapter
34252  * @extends Roo.menu.BaseItem
34253  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34254  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34255  * @constructor
34256  * Creates a new Adapter
34257  * @param {Object} config Configuration options
34258  */
34259 Roo.menu.Adapter = function(component, config){
34260     Roo.menu.Adapter.superclass.constructor.call(this, config);
34261     this.component = component;
34262 };
34263 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34264     // private
34265     canActivate : true,
34266
34267     // private
34268     onRender : function(container, position){
34269         this.component.render(container);
34270         this.el = this.component.getEl();
34271     },
34272
34273     // private
34274     activate : function(){
34275         if(this.disabled){
34276             return false;
34277         }
34278         this.component.focus();
34279         this.fireEvent("activate", this);
34280         return true;
34281     },
34282
34283     // private
34284     deactivate : function(){
34285         this.fireEvent("deactivate", this);
34286     },
34287
34288     // private
34289     disable : function(){
34290         this.component.disable();
34291         Roo.menu.Adapter.superclass.disable.call(this);
34292     },
34293
34294     // private
34295     enable : function(){
34296         this.component.enable();
34297         Roo.menu.Adapter.superclass.enable.call(this);
34298     }
34299 });/*
34300  * Based on:
34301  * Ext JS Library 1.1.1
34302  * Copyright(c) 2006-2007, Ext JS, LLC.
34303  *
34304  * Originally Released Under LGPL - original licence link has changed is not relivant.
34305  *
34306  * Fork - LGPL
34307  * <script type="text/javascript">
34308  */
34309
34310 /**
34311  * @class Roo.menu.TextItem
34312  * @extends Roo.menu.BaseItem
34313  * Adds a static text string to a menu, usually used as either a heading or group separator.
34314  * @constructor
34315  * Creates a new TextItem
34316  * @param {String} text The text to display
34317  */
34318 Roo.menu.TextItem = function(text){
34319     this.text = text;
34320     Roo.menu.TextItem.superclass.constructor.call(this);
34321 };
34322
34323 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34324     /**
34325      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34326      */
34327     hideOnClick : false,
34328     /**
34329      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34330      */
34331     itemCls : "x-menu-text",
34332
34333     // private
34334     onRender : function(){
34335         var s = document.createElement("span");
34336         s.className = this.itemCls;
34337         s.innerHTML = this.text;
34338         this.el = s;
34339         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34340     }
34341 });/*
34342  * Based on:
34343  * Ext JS Library 1.1.1
34344  * Copyright(c) 2006-2007, Ext JS, LLC.
34345  *
34346  * Originally Released Under LGPL - original licence link has changed is not relivant.
34347  *
34348  * Fork - LGPL
34349  * <script type="text/javascript">
34350  */
34351
34352 /**
34353  * @class Roo.menu.Separator
34354  * @extends Roo.menu.BaseItem
34355  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34356  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34357  * @constructor
34358  * @param {Object} config Configuration options
34359  */
34360 Roo.menu.Separator = function(config){
34361     Roo.menu.Separator.superclass.constructor.call(this, config);
34362 };
34363
34364 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34365     /**
34366      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34367      */
34368     itemCls : "x-menu-sep",
34369     /**
34370      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34371      */
34372     hideOnClick : false,
34373
34374     // private
34375     onRender : function(li){
34376         var s = document.createElement("span");
34377         s.className = this.itemCls;
34378         s.innerHTML = "&#160;";
34379         this.el = s;
34380         li.addClass("x-menu-sep-li");
34381         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34382     }
34383 });/*
34384  * Based on:
34385  * Ext JS Library 1.1.1
34386  * Copyright(c) 2006-2007, Ext JS, LLC.
34387  *
34388  * Originally Released Under LGPL - original licence link has changed is not relivant.
34389  *
34390  * Fork - LGPL
34391  * <script type="text/javascript">
34392  */
34393 /**
34394  * @class Roo.menu.Item
34395  * @extends Roo.menu.BaseItem
34396  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34397  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34398  * activation and click handling.
34399  * @constructor
34400  * Creates a new Item
34401  * @param {Object} config Configuration options
34402  */
34403 Roo.menu.Item = function(config){
34404     Roo.menu.Item.superclass.constructor.call(this, config);
34405     if(this.menu){
34406         this.menu = Roo.menu.MenuMgr.get(this.menu);
34407     }
34408 };
34409 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34410     /**
34411      * @cfg {String} icon
34412      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34413      */
34414     /**
34415      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34416      */
34417     itemCls : "x-menu-item",
34418     /**
34419      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34420      */
34421     canActivate : true,
34422     /**
34423      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34424      */
34425     showDelay: 200,
34426     // doc'd in BaseItem
34427     hideDelay: 200,
34428
34429     // private
34430     ctype: "Roo.menu.Item",
34431     
34432     // private
34433     onRender : function(container, position){
34434         var el = document.createElement("a");
34435         el.hideFocus = true;
34436         el.unselectable = "on";
34437         el.href = this.href || "#";
34438         if(this.hrefTarget){
34439             el.target = this.hrefTarget;
34440         }
34441         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34442         el.innerHTML = String.format(
34443                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34444                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34445         this.el = el;
34446         Roo.menu.Item.superclass.onRender.call(this, container, position);
34447     },
34448
34449     /**
34450      * Sets the text to display in this menu item
34451      * @param {String} text The text to display
34452      */
34453     setText : function(text){
34454         this.text = text;
34455         if(this.rendered){
34456             this.el.update(String.format(
34457                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34458                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34459             this.parentMenu.autoWidth();
34460         }
34461     },
34462
34463     // private
34464     handleClick : function(e){
34465         if(!this.href){ // if no link defined, stop the event automatically
34466             e.stopEvent();
34467         }
34468         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34469     },
34470
34471     // private
34472     activate : function(autoExpand){
34473         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34474             this.focus();
34475             if(autoExpand){
34476                 this.expandMenu();
34477             }
34478         }
34479         return true;
34480     },
34481
34482     // private
34483     shouldDeactivate : function(e){
34484         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34485             if(this.menu && this.menu.isVisible()){
34486                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34487             }
34488             return true;
34489         }
34490         return false;
34491     },
34492
34493     // private
34494     deactivate : function(){
34495         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34496         this.hideMenu();
34497     },
34498
34499     // private
34500     expandMenu : function(autoActivate){
34501         if(!this.disabled && this.menu){
34502             clearTimeout(this.hideTimer);
34503             delete this.hideTimer;
34504             if(!this.menu.isVisible() && !this.showTimer){
34505                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34506             }else if (this.menu.isVisible() && autoActivate){
34507                 this.menu.tryActivate(0, 1);
34508             }
34509         }
34510     },
34511
34512     // private
34513     deferExpand : function(autoActivate){
34514         delete this.showTimer;
34515         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34516         if(autoActivate){
34517             this.menu.tryActivate(0, 1);
34518         }
34519     },
34520
34521     // private
34522     hideMenu : function(){
34523         clearTimeout(this.showTimer);
34524         delete this.showTimer;
34525         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34526             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34527         }
34528     },
34529
34530     // private
34531     deferHide : function(){
34532         delete this.hideTimer;
34533         this.menu.hide();
34534     }
34535 });/*
34536  * Based on:
34537  * Ext JS Library 1.1.1
34538  * Copyright(c) 2006-2007, Ext JS, LLC.
34539  *
34540  * Originally Released Under LGPL - original licence link has changed is not relivant.
34541  *
34542  * Fork - LGPL
34543  * <script type="text/javascript">
34544  */
34545  
34546 /**
34547  * @class Roo.menu.CheckItem
34548  * @extends Roo.menu.Item
34549  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34550  * @constructor
34551  * Creates a new CheckItem
34552  * @param {Object} config Configuration options
34553  */
34554 Roo.menu.CheckItem = function(config){
34555     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34556     this.addEvents({
34557         /**
34558          * @event beforecheckchange
34559          * Fires before the checked value is set, providing an opportunity to cancel if needed
34560          * @param {Roo.menu.CheckItem} this
34561          * @param {Boolean} checked The new checked value that will be set
34562          */
34563         "beforecheckchange" : true,
34564         /**
34565          * @event checkchange
34566          * Fires after the checked value has been set
34567          * @param {Roo.menu.CheckItem} this
34568          * @param {Boolean} checked The checked value that was set
34569          */
34570         "checkchange" : true
34571     });
34572     if(this.checkHandler){
34573         this.on('checkchange', this.checkHandler, this.scope);
34574     }
34575 };
34576 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34577     /**
34578      * @cfg {String} group
34579      * All check items with the same group name will automatically be grouped into a single-select
34580      * radio button group (defaults to '')
34581      */
34582     /**
34583      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34584      */
34585     itemCls : "x-menu-item x-menu-check-item",
34586     /**
34587      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34588      */
34589     groupClass : "x-menu-group-item",
34590
34591     /**
34592      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34593      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34594      * initialized with checked = true will be rendered as checked.
34595      */
34596     checked: false,
34597
34598     // private
34599     ctype: "Roo.menu.CheckItem",
34600
34601     // private
34602     onRender : function(c){
34603         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34604         if(this.group){
34605             this.el.addClass(this.groupClass);
34606         }
34607         Roo.menu.MenuMgr.registerCheckable(this);
34608         if(this.checked){
34609             this.checked = false;
34610             this.setChecked(true, true);
34611         }
34612     },
34613
34614     // private
34615     destroy : function(){
34616         if(this.rendered){
34617             Roo.menu.MenuMgr.unregisterCheckable(this);
34618         }
34619         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34620     },
34621
34622     /**
34623      * Set the checked state of this item
34624      * @param {Boolean} checked The new checked value
34625      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34626      */
34627     setChecked : function(state, suppressEvent){
34628         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34629             if(this.container){
34630                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34631             }
34632             this.checked = state;
34633             if(suppressEvent !== true){
34634                 this.fireEvent("checkchange", this, state);
34635             }
34636         }
34637     },
34638
34639     // private
34640     handleClick : function(e){
34641        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34642            this.setChecked(!this.checked);
34643        }
34644        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34645     }
34646 });/*
34647  * Based on:
34648  * Ext JS Library 1.1.1
34649  * Copyright(c) 2006-2007, Ext JS, LLC.
34650  *
34651  * Originally Released Under LGPL - original licence link has changed is not relivant.
34652  *
34653  * Fork - LGPL
34654  * <script type="text/javascript">
34655  */
34656  
34657 /**
34658  * @class Roo.menu.DateItem
34659  * @extends Roo.menu.Adapter
34660  * A menu item that wraps the {@link Roo.DatPicker} component.
34661  * @constructor
34662  * Creates a new DateItem
34663  * @param {Object} config Configuration options
34664  */
34665 Roo.menu.DateItem = function(config){
34666     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34667     /** The Roo.DatePicker object @type Roo.DatePicker */
34668     this.picker = this.component;
34669     this.addEvents({select: true});
34670     
34671     this.picker.on("render", function(picker){
34672         picker.getEl().swallowEvent("click");
34673         picker.container.addClass("x-menu-date-item");
34674     });
34675
34676     this.picker.on("select", this.onSelect, this);
34677 };
34678
34679 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34680     // private
34681     onSelect : function(picker, date){
34682         this.fireEvent("select", this, date, picker);
34683         Roo.menu.DateItem.superclass.handleClick.call(this);
34684     }
34685 });/*
34686  * Based on:
34687  * Ext JS Library 1.1.1
34688  * Copyright(c) 2006-2007, Ext JS, LLC.
34689  *
34690  * Originally Released Under LGPL - original licence link has changed is not relivant.
34691  *
34692  * Fork - LGPL
34693  * <script type="text/javascript">
34694  */
34695  
34696 /**
34697  * @class Roo.menu.ColorItem
34698  * @extends Roo.menu.Adapter
34699  * A menu item that wraps the {@link Roo.ColorPalette} component.
34700  * @constructor
34701  * Creates a new ColorItem
34702  * @param {Object} config Configuration options
34703  */
34704 Roo.menu.ColorItem = function(config){
34705     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34706     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34707     this.palette = this.component;
34708     this.relayEvents(this.palette, ["select"]);
34709     if(this.selectHandler){
34710         this.on('select', this.selectHandler, this.scope);
34711     }
34712 };
34713 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34714  * Based on:
34715  * Ext JS Library 1.1.1
34716  * Copyright(c) 2006-2007, Ext JS, LLC.
34717  *
34718  * Originally Released Under LGPL - original licence link has changed is not relivant.
34719  *
34720  * Fork - LGPL
34721  * <script type="text/javascript">
34722  */
34723  
34724
34725 /**
34726  * @class Roo.menu.DateMenu
34727  * @extends Roo.menu.Menu
34728  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34729  * @constructor
34730  * Creates a new DateMenu
34731  * @param {Object} config Configuration options
34732  */
34733 Roo.menu.DateMenu = function(config){
34734     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34735     this.plain = true;
34736     var di = new Roo.menu.DateItem(config);
34737     this.add(di);
34738     /**
34739      * The {@link Roo.DatePicker} instance for this DateMenu
34740      * @type DatePicker
34741      */
34742     this.picker = di.picker;
34743     /**
34744      * @event select
34745      * @param {DatePicker} picker
34746      * @param {Date} date
34747      */
34748     this.relayEvents(di, ["select"]);
34749
34750     this.on('beforeshow', function(){
34751         if(this.picker){
34752             this.picker.hideMonthPicker(true);
34753         }
34754     }, this);
34755 };
34756 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34757     cls:'x-date-menu'
34758 });/*
34759  * Based on:
34760  * Ext JS Library 1.1.1
34761  * Copyright(c) 2006-2007, Ext JS, LLC.
34762  *
34763  * Originally Released Under LGPL - original licence link has changed is not relivant.
34764  *
34765  * Fork - LGPL
34766  * <script type="text/javascript">
34767  */
34768  
34769
34770 /**
34771  * @class Roo.menu.ColorMenu
34772  * @extends Roo.menu.Menu
34773  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34774  * @constructor
34775  * Creates a new ColorMenu
34776  * @param {Object} config Configuration options
34777  */
34778 Roo.menu.ColorMenu = function(config){
34779     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34780     this.plain = true;
34781     var ci = new Roo.menu.ColorItem(config);
34782     this.add(ci);
34783     /**
34784      * The {@link Roo.ColorPalette} instance for this ColorMenu
34785      * @type ColorPalette
34786      */
34787     this.palette = ci.palette;
34788     /**
34789      * @event select
34790      * @param {ColorPalette} palette
34791      * @param {String} color
34792      */
34793     this.relayEvents(ci, ["select"]);
34794 };
34795 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34796  * Based on:
34797  * Ext JS Library 1.1.1
34798  * Copyright(c) 2006-2007, Ext JS, LLC.
34799  *
34800  * Originally Released Under LGPL - original licence link has changed is not relivant.
34801  *
34802  * Fork - LGPL
34803  * <script type="text/javascript">
34804  */
34805  
34806 /**
34807  * @class Roo.form.Field
34808  * @extends Roo.BoxComponent
34809  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34810  * @constructor
34811  * Creates a new Field
34812  * @param {Object} config Configuration options
34813  */
34814 Roo.form.Field = function(config){
34815     Roo.form.Field.superclass.constructor.call(this, config);
34816 };
34817
34818 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34819     /**
34820      * @cfg {String} fieldLabel Label to use when rendering a form.
34821      */
34822        /**
34823      * @cfg {String} qtip Mouse over tip
34824      */
34825      
34826     /**
34827      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34828      */
34829     invalidClass : "x-form-invalid",
34830     /**
34831      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
34832      */
34833     invalidText : "The value in this field is invalid",
34834     /**
34835      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34836      */
34837     focusClass : "x-form-focus",
34838     /**
34839      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34840       automatic validation (defaults to "keyup").
34841      */
34842     validationEvent : "keyup",
34843     /**
34844      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34845      */
34846     validateOnBlur : true,
34847     /**
34848      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34849      */
34850     validationDelay : 250,
34851     /**
34852      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34853      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34854      */
34855     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34856     /**
34857      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34858      */
34859     fieldClass : "x-form-field",
34860     /**
34861      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34862      *<pre>
34863 Value         Description
34864 -----------   ----------------------------------------------------------------------
34865 qtip          Display a quick tip when the user hovers over the field
34866 title         Display a default browser title attribute popup
34867 under         Add a block div beneath the field containing the error text
34868 side          Add an error icon to the right of the field with a popup on hover
34869 [element id]  Add the error text directly to the innerHTML of the specified element
34870 </pre>
34871      */
34872     msgTarget : 'qtip',
34873     /**
34874      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34875      */
34876     msgFx : 'normal',
34877
34878     /**
34879      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
34880      */
34881     readOnly : false,
34882
34883     /**
34884      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34885      */
34886     disabled : false,
34887
34888     /**
34889      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34890      */
34891     inputType : undefined,
34892     
34893     /**
34894      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
34895          */
34896         tabIndex : undefined,
34897         
34898     // private
34899     isFormField : true,
34900
34901     // private
34902     hasFocus : false,
34903     /**
34904      * @property {Roo.Element} fieldEl
34905      * Element Containing the rendered Field (with label etc.)
34906      */
34907     /**
34908      * @cfg {Mixed} value A value to initialize this field with.
34909      */
34910     value : undefined,
34911
34912     /**
34913      * @cfg {String} name The field's HTML name attribute.
34914      */
34915     /**
34916      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34917      */
34918
34919         // private ??
34920         initComponent : function(){
34921         Roo.form.Field.superclass.initComponent.call(this);
34922         this.addEvents({
34923             /**
34924              * @event focus
34925              * Fires when this field receives input focus.
34926              * @param {Roo.form.Field} this
34927              */
34928             focus : true,
34929             /**
34930              * @event blur
34931              * Fires when this field loses input focus.
34932              * @param {Roo.form.Field} this
34933              */
34934             blur : true,
34935             /**
34936              * @event specialkey
34937              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
34938              * {@link Roo.EventObject#getKey} to determine which key was pressed.
34939              * @param {Roo.form.Field} this
34940              * @param {Roo.EventObject} e The event object
34941              */
34942             specialkey : true,
34943             /**
34944              * @event change
34945              * Fires just before the field blurs if the field value has changed.
34946              * @param {Roo.form.Field} this
34947              * @param {Mixed} newValue The new value
34948              * @param {Mixed} oldValue The original value
34949              */
34950             change : true,
34951             /**
34952              * @event invalid
34953              * Fires after the field has been marked as invalid.
34954              * @param {Roo.form.Field} this
34955              * @param {String} msg The validation message
34956              */
34957             invalid : true,
34958             /**
34959              * @event valid
34960              * Fires after the field has been validated with no errors.
34961              * @param {Roo.form.Field} this
34962              */
34963             valid : true
34964         });
34965     },
34966
34967     /**
34968      * Returns the name attribute of the field if available
34969      * @return {String} name The field name
34970      */
34971     getName: function(){
34972          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
34973     },
34974
34975     // private
34976     onRender : function(ct, position){
34977         Roo.form.Field.superclass.onRender.call(this, ct, position);
34978         if(!this.el){
34979             var cfg = this.getAutoCreate();
34980             if(!cfg.name){
34981                 cfg.name = this.name || this.id;
34982             }
34983             if(this.inputType){
34984                 cfg.type = this.inputType;
34985             }
34986             this.el = ct.createChild(cfg, position);
34987         }
34988         var type = this.el.dom.type;
34989         if(type){
34990             if(type == 'password'){
34991                 type = 'text';
34992             }
34993             this.el.addClass('x-form-'+type);
34994         }
34995         if(this.readOnly){
34996             this.el.dom.readOnly = true;
34997         }
34998         if(this.tabIndex !== undefined){
34999             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35000         }
35001
35002         this.el.addClass([this.fieldClass, this.cls]);
35003         this.initValue();
35004     },
35005
35006     /**
35007      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35008      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35009      * @return {Roo.form.Field} this
35010      */
35011     applyTo : function(target){
35012         this.allowDomMove = false;
35013         this.el = Roo.get(target);
35014         this.render(this.el.dom.parentNode);
35015         return this;
35016     },
35017
35018     // private
35019     initValue : function(){
35020         if(this.value !== undefined){
35021             this.setValue(this.value);
35022         }else if(this.el.dom.value.length > 0){
35023             this.setValue(this.el.dom.value);
35024         }
35025     },
35026
35027     /**
35028      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35029      */
35030     isDirty : function() {
35031         if(this.disabled) {
35032             return false;
35033         }
35034         return String(this.getValue()) !== String(this.originalValue);
35035     },
35036
35037     // private
35038     afterRender : function(){
35039         Roo.form.Field.superclass.afterRender.call(this);
35040         this.initEvents();
35041     },
35042
35043     // private
35044     fireKey : function(e){
35045         if(e.isNavKeyPress()){
35046             this.fireEvent("specialkey", this, e);
35047         }
35048     },
35049
35050     /**
35051      * Resets the current field value to the originally loaded value and clears any validation messages
35052      */
35053     reset : function(){
35054         this.setValue(this.originalValue);
35055         this.clearInvalid();
35056     },
35057
35058     // private
35059     initEvents : function(){
35060         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35061         this.el.on("focus", this.onFocus,  this);
35062         this.el.on("blur", this.onBlur,  this);
35063
35064         // reference to original value for reset
35065         this.originalValue = this.getValue();
35066     },
35067
35068     // private
35069     onFocus : function(){
35070         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35071             this.el.addClass(this.focusClass);
35072         }
35073         if(!this.hasFocus){
35074             this.hasFocus = true;
35075             this.startValue = this.getValue();
35076             this.fireEvent("focus", this);
35077         }
35078     },
35079
35080     beforeBlur : Roo.emptyFn,
35081
35082     // private
35083     onBlur : function(){
35084         this.beforeBlur();
35085         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35086             this.el.removeClass(this.focusClass);
35087         }
35088         this.hasFocus = false;
35089         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35090             this.validate();
35091         }
35092         var v = this.getValue();
35093         if(String(v) !== String(this.startValue)){
35094             this.fireEvent('change', this, v, this.startValue);
35095         }
35096         this.fireEvent("blur", this);
35097     },
35098
35099     /**
35100      * Returns whether or not the field value is currently valid
35101      * @param {Boolean} preventMark True to disable marking the field invalid
35102      * @return {Boolean} True if the value is valid, else false
35103      */
35104     isValid : function(preventMark){
35105         if(this.disabled){
35106             return true;
35107         }
35108         var restore = this.preventMark;
35109         this.preventMark = preventMark === true;
35110         var v = this.validateValue(this.processValue(this.getRawValue()));
35111         this.preventMark = restore;
35112         return v;
35113     },
35114
35115     /**
35116      * Validates the field value
35117      * @return {Boolean} True if the value is valid, else false
35118      */
35119     validate : function(){
35120         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35121             this.clearInvalid();
35122             return true;
35123         }
35124         return false;
35125     },
35126
35127     processValue : function(value){
35128         return value;
35129     },
35130
35131     // private
35132     // Subclasses should provide the validation implementation by overriding this
35133     validateValue : function(value){
35134         return true;
35135     },
35136
35137     /**
35138      * Mark this field as invalid
35139      * @param {String} msg The validation message
35140      */
35141     markInvalid : function(msg){
35142         if(!this.rendered || this.preventMark){ // not rendered
35143             return;
35144         }
35145         this.el.addClass(this.invalidClass);
35146         msg = msg || this.invalidText;
35147         switch(this.msgTarget){
35148             case 'qtip':
35149                 this.el.dom.qtip = msg;
35150                 this.el.dom.qclass = 'x-form-invalid-tip';
35151                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35152                     Roo.QuickTips.enable();
35153                 }
35154                 break;
35155             case 'title':
35156                 this.el.dom.title = msg;
35157                 break;
35158             case 'under':
35159                 if(!this.errorEl){
35160                     var elp = this.el.findParent('.x-form-element', 5, true);
35161                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35162                     this.errorEl.setWidth(elp.getWidth(true)-20);
35163                 }
35164                 this.errorEl.update(msg);
35165                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35166                 break;
35167             case 'side':
35168                 if(!this.errorIcon){
35169                     var elp = this.el.findParent('.x-form-element', 5, true);
35170                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35171                 }
35172                 this.alignErrorIcon();
35173                 this.errorIcon.dom.qtip = msg;
35174                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35175                 this.errorIcon.show();
35176                 this.on('resize', this.alignErrorIcon, this);
35177                 break;
35178             default:
35179                 var t = Roo.getDom(this.msgTarget);
35180                 t.innerHTML = msg;
35181                 t.style.display = this.msgDisplay;
35182                 break;
35183         }
35184         this.fireEvent('invalid', this, msg);
35185     },
35186
35187     // private
35188     alignErrorIcon : function(){
35189         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35190     },
35191
35192     /**
35193      * Clear any invalid styles/messages for this field
35194      */
35195     clearInvalid : function(){
35196         if(!this.rendered || this.preventMark){ // not rendered
35197             return;
35198         }
35199         this.el.removeClass(this.invalidClass);
35200         switch(this.msgTarget){
35201             case 'qtip':
35202                 this.el.dom.qtip = '';
35203                 break;
35204             case 'title':
35205                 this.el.dom.title = '';
35206                 break;
35207             case 'under':
35208                 if(this.errorEl){
35209                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35210                 }
35211                 break;
35212             case 'side':
35213                 if(this.errorIcon){
35214                     this.errorIcon.dom.qtip = '';
35215                     this.errorIcon.hide();
35216                     this.un('resize', this.alignErrorIcon, this);
35217                 }
35218                 break;
35219             default:
35220                 var t = Roo.getDom(this.msgTarget);
35221                 t.innerHTML = '';
35222                 t.style.display = 'none';
35223                 break;
35224         }
35225         this.fireEvent('valid', this);
35226     },
35227
35228     /**
35229      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35230      * @return {Mixed} value The field value
35231      */
35232     getRawValue : function(){
35233         var v = this.el.getValue();
35234         if(v === this.emptyText){
35235             v = '';
35236         }
35237         return v;
35238     },
35239
35240     /**
35241      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35242      * @return {Mixed} value The field value
35243      */
35244     getValue : function(){
35245         var v = this.el.getValue();
35246         if(v === this.emptyText || v === undefined){
35247             v = '';
35248         }
35249         return v;
35250     },
35251
35252     /**
35253      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35254      * @param {Mixed} value The value to set
35255      */
35256     setRawValue : function(v){
35257         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35258     },
35259
35260     /**
35261      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35262      * @param {Mixed} value The value to set
35263      */
35264     setValue : function(v){
35265         this.value = v;
35266         if(this.rendered){
35267             this.el.dom.value = (v === null || v === undefined ? '' : v);
35268             this.validate();
35269         }
35270     },
35271
35272     adjustSize : function(w, h){
35273         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35274         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35275         return s;
35276     },
35277
35278     adjustWidth : function(tag, w){
35279         tag = tag.toLowerCase();
35280         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35281             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35282                 if(tag == 'input'){
35283                     return w + 2;
35284                 }
35285                 if(tag = 'textarea'){
35286                     return w-2;
35287                 }
35288             }else if(Roo.isOpera){
35289                 if(tag == 'input'){
35290                     return w + 2;
35291                 }
35292                 if(tag = 'textarea'){
35293                     return w-2;
35294                 }
35295             }
35296         }
35297         return w;
35298     }
35299 });
35300
35301
35302 // anything other than normal should be considered experimental
35303 Roo.form.Field.msgFx = {
35304     normal : {
35305         show: function(msgEl, f){
35306             msgEl.setDisplayed('block');
35307         },
35308
35309         hide : function(msgEl, f){
35310             msgEl.setDisplayed(false).update('');
35311         }
35312     },
35313
35314     slide : {
35315         show: function(msgEl, f){
35316             msgEl.slideIn('t', {stopFx:true});
35317         },
35318
35319         hide : function(msgEl, f){
35320             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35321         }
35322     },
35323
35324     slideRight : {
35325         show: function(msgEl, f){
35326             msgEl.fixDisplay();
35327             msgEl.alignTo(f.el, 'tl-tr');
35328             msgEl.slideIn('l', {stopFx:true});
35329         },
35330
35331         hide : function(msgEl, f){
35332             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35333         }
35334     }
35335 };/*
35336  * Based on:
35337  * Ext JS Library 1.1.1
35338  * Copyright(c) 2006-2007, Ext JS, LLC.
35339  *
35340  * Originally Released Under LGPL - original licence link has changed is not relivant.
35341  *
35342  * Fork - LGPL
35343  * <script type="text/javascript">
35344  */
35345  
35346
35347 /**
35348  * @class Roo.form.TextField
35349  * @extends Roo.form.Field
35350  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35351  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35352  * @constructor
35353  * Creates a new TextField
35354  * @param {Object} config Configuration options
35355  */
35356 Roo.form.TextField = function(config){
35357     Roo.form.TextField.superclass.constructor.call(this, config);
35358     this.addEvents({
35359         /**
35360          * @event autosize
35361          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35362          * according to the default logic, but this event provides a hook for the developer to apply additional
35363          * logic at runtime to resize the field if needed.
35364              * @param {Roo.form.Field} this This text field
35365              * @param {Number} width The new field width
35366              */
35367         autosize : true
35368     });
35369 };
35370
35371 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35372     /**
35373      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35374      */
35375     grow : false,
35376     /**
35377      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35378      */
35379     growMin : 30,
35380     /**
35381      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35382      */
35383     growMax : 800,
35384     /**
35385      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35386      */
35387     vtype : null,
35388     /**
35389      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35390      */
35391     maskRe : null,
35392     /**
35393      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35394      */
35395     disableKeyFilter : false,
35396     /**
35397      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35398      */
35399     allowBlank : true,
35400     /**
35401      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35402      */
35403     minLength : 0,
35404     /**
35405      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35406      */
35407     maxLength : Number.MAX_VALUE,
35408     /**
35409      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35410      */
35411     minLengthText : "The minimum length for this field is {0}",
35412     /**
35413      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35414      */
35415     maxLengthText : "The maximum length for this field is {0}",
35416     /**
35417      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35418      */
35419     selectOnFocus : false,
35420     /**
35421      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35422      */
35423     blankText : "This field is required",
35424     /**
35425      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35426      * If available, this function will be called only after the basic validators all return true, and will be passed the
35427      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35428      */
35429     validator : null,
35430     /**
35431      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35432      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35433      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35434      */
35435     regex : null,
35436     /**
35437      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35438      */
35439     regexText : "",
35440     /**
35441      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35442      */
35443     emptyText : null,
35444     /**
35445      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35446      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35447      */
35448     emptyClass : 'x-form-empty-field',
35449
35450     // private
35451     initEvents : function(){
35452         Roo.form.TextField.superclass.initEvents.call(this);
35453         if(this.validationEvent == 'keyup'){
35454             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35455             this.el.on('keyup', this.filterValidation, this);
35456         }
35457         else if(this.validationEvent !== false){
35458             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35459         }
35460         if(this.selectOnFocus || this.emptyText){
35461             this.on("focus", this.preFocus, this);
35462             if(this.emptyText){
35463                 this.on('blur', this.postBlur, this);
35464                 this.applyEmptyText();
35465             }
35466         }
35467         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35468             this.el.on("keypress", this.filterKeys, this);
35469         }
35470         if(this.grow){
35471             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35472             this.el.on("click", this.autoSize,  this);
35473         }
35474     },
35475
35476     processValue : function(value){
35477         if(this.stripCharsRe){
35478             var newValue = value.replace(this.stripCharsRe, '');
35479             if(newValue !== value){
35480                 this.setRawValue(newValue);
35481                 return newValue;
35482             }
35483         }
35484         return value;
35485     },
35486
35487     filterValidation : function(e){
35488         if(!e.isNavKeyPress()){
35489             this.validationTask.delay(this.validationDelay);
35490         }
35491     },
35492
35493     // private
35494     onKeyUp : function(e){
35495         if(!e.isNavKeyPress()){
35496             this.autoSize();
35497         }
35498     },
35499
35500     /**
35501      * Resets the current field value to the originally-loaded value and clears any validation messages.
35502      * Also adds emptyText and emptyClass if the original value was blank.
35503      */
35504     reset : function(){
35505         Roo.form.TextField.superclass.reset.call(this);
35506         this.applyEmptyText();
35507     },
35508
35509     applyEmptyText : function(){
35510         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35511             this.setRawValue(this.emptyText);
35512             this.el.addClass(this.emptyClass);
35513         }
35514     },
35515
35516     // private
35517     preFocus : function(){
35518         if(this.emptyText){
35519             if(this.el.dom.value == this.emptyText){
35520                 this.setRawValue('');
35521             }
35522             this.el.removeClass(this.emptyClass);
35523         }
35524         if(this.selectOnFocus){
35525             this.el.dom.select();
35526         }
35527     },
35528
35529     // private
35530     postBlur : function(){
35531         this.applyEmptyText();
35532     },
35533
35534     // private
35535     filterKeys : function(e){
35536         var k = e.getKey();
35537         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35538             return;
35539         }
35540         var c = e.getCharCode(), cc = String.fromCharCode(c);
35541         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35542             return;
35543         }
35544         if(!this.maskRe.test(cc)){
35545             e.stopEvent();
35546         }
35547     },
35548
35549     setValue : function(v){
35550         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35551             this.el.removeClass(this.emptyClass);
35552         }
35553         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35554         this.applyEmptyText();
35555         this.autoSize();
35556     },
35557
35558     /**
35559      * Validates a value according to the field's validation rules and marks the field as invalid
35560      * if the validation fails
35561      * @param {Mixed} value The value to validate
35562      * @return {Boolean} True if the value is valid, else false
35563      */
35564     validateValue : function(value){
35565         if(value.length < 1 || value === this.emptyText){ // if it's blank
35566              if(this.allowBlank){
35567                 this.clearInvalid();
35568                 return true;
35569              }else{
35570                 this.markInvalid(this.blankText);
35571                 return false;
35572              }
35573         }
35574         if(value.length < this.minLength){
35575             this.markInvalid(String.format(this.minLengthText, this.minLength));
35576             return false;
35577         }
35578         if(value.length > this.maxLength){
35579             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35580             return false;
35581         }
35582         if(this.vtype){
35583             var vt = Roo.form.VTypes;
35584             if(!vt[this.vtype](value, this)){
35585                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35586                 return false;
35587             }
35588         }
35589         if(typeof this.validator == "function"){
35590             var msg = this.validator(value);
35591             if(msg !== true){
35592                 this.markInvalid(msg);
35593                 return false;
35594             }
35595         }
35596         if(this.regex && !this.regex.test(value)){
35597             this.markInvalid(this.regexText);
35598             return false;
35599         }
35600         return true;
35601     },
35602
35603     /**
35604      * Selects text in this field
35605      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35606      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35607      */
35608     selectText : function(start, end){
35609         var v = this.getRawValue();
35610         if(v.length > 0){
35611             start = start === undefined ? 0 : start;
35612             end = end === undefined ? v.length : end;
35613             var d = this.el.dom;
35614             if(d.setSelectionRange){
35615                 d.setSelectionRange(start, end);
35616             }else if(d.createTextRange){
35617                 var range = d.createTextRange();
35618                 range.moveStart("character", start);
35619                 range.moveEnd("character", v.length-end);
35620                 range.select();
35621             }
35622         }
35623     },
35624
35625     /**
35626      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35627      * This only takes effect if grow = true, and fires the autosize event.
35628      */
35629     autoSize : function(){
35630         if(!this.grow || !this.rendered){
35631             return;
35632         }
35633         if(!this.metrics){
35634             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35635         }
35636         var el = this.el;
35637         var v = el.dom.value;
35638         var d = document.createElement('div');
35639         d.appendChild(document.createTextNode(v));
35640         v = d.innerHTML;
35641         d = null;
35642         v += "&#160;";
35643         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35644         this.el.setWidth(w);
35645         this.fireEvent("autosize", this, w);
35646     }
35647 });/*
35648  * Based on:
35649  * Ext JS Library 1.1.1
35650  * Copyright(c) 2006-2007, Ext JS, LLC.
35651  *
35652  * Originally Released Under LGPL - original licence link has changed is not relivant.
35653  *
35654  * Fork - LGPL
35655  * <script type="text/javascript">
35656  */
35657  
35658 /**
35659  * @class Roo.form.Hidden
35660  * @extends Roo.form.TextField
35661  * Simple Hidden element used on forms 
35662  * 
35663  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35664  * 
35665  * @constructor
35666  * Creates a new Hidden form element.
35667  * @param {Object} config Configuration options
35668  */
35669
35670
35671
35672 // easy hidden field...
35673 Roo.form.Hidden = function(config){
35674     Roo.form.Hidden.superclass.constructor.call(this, config);
35675 };
35676   
35677 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35678     fieldLabel:      '',
35679     inputType:      'hidden',
35680     width:          50,
35681     allowBlank:     true,
35682     labelSeparator: '',
35683     hidden:         true,
35684     itemCls :       'x-form-item-display-none'
35685
35686
35687 });
35688
35689
35690 /*
35691  * Based on:
35692  * Ext JS Library 1.1.1
35693  * Copyright(c) 2006-2007, Ext JS, LLC.
35694  *
35695  * Originally Released Under LGPL - original licence link has changed is not relivant.
35696  *
35697  * Fork - LGPL
35698  * <script type="text/javascript">
35699  */
35700  
35701 /**
35702  * @class Roo.form.TriggerField
35703  * @extends Roo.form.TextField
35704  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35705  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35706  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35707  * for which you can provide a custom implementation.  For example:
35708  * <pre><code>
35709 var trigger = new Roo.form.TriggerField();
35710 trigger.onTriggerClick = myTriggerFn;
35711 trigger.applyTo('my-field');
35712 </code></pre>
35713  *
35714  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35715  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35716  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35717  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35718  * @constructor
35719  * Create a new TriggerField.
35720  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35721  * to the base TextField)
35722  */
35723 Roo.form.TriggerField = function(config){
35724     this.mimicing = false;
35725     Roo.form.TriggerField.superclass.constructor.call(this, config);
35726 };
35727
35728 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35729     /**
35730      * @cfg {String} triggerClass A CSS class to apply to the trigger
35731      */
35732     /**
35733      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35734      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35735      */
35736     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35737     /**
35738      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35739      */
35740     hideTrigger:false,
35741
35742     /** @cfg {Boolean} grow @hide */
35743     /** @cfg {Number} growMin @hide */
35744     /** @cfg {Number} growMax @hide */
35745
35746     /**
35747      * @hide 
35748      * @method
35749      */
35750     autoSize: Roo.emptyFn,
35751     // private
35752     monitorTab : true,
35753     // private
35754     deferHeight : true,
35755
35756     
35757     actionMode : 'wrap',
35758     // private
35759     onResize : function(w, h){
35760         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35761         if(typeof w == 'number'){
35762             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35763         }
35764     },
35765
35766     // private
35767     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35768
35769     // private
35770     getResizeEl : function(){
35771         return this.wrap;
35772     },
35773
35774     // private
35775     getPositionEl : function(){
35776         return this.wrap;
35777     },
35778
35779     // private
35780     alignErrorIcon : function(){
35781         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35782     },
35783
35784     // private
35785     onRender : function(ct, position){
35786         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35787         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35788         this.trigger = this.wrap.createChild(this.triggerConfig ||
35789                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35790         if(this.hideTrigger){
35791             this.trigger.setDisplayed(false);
35792         }
35793         this.initTrigger();
35794         if(!this.width){
35795             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35796         }
35797     },
35798
35799     // private
35800     initTrigger : function(){
35801         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35802         this.trigger.addClassOnOver('x-form-trigger-over');
35803         this.trigger.addClassOnClick('x-form-trigger-click');
35804     },
35805
35806     // private
35807     onDestroy : function(){
35808         if(this.trigger){
35809             this.trigger.removeAllListeners();
35810             this.trigger.remove();
35811         }
35812         if(this.wrap){
35813             this.wrap.remove();
35814         }
35815         Roo.form.TriggerField.superclass.onDestroy.call(this);
35816     },
35817
35818     // private
35819     onFocus : function(){
35820         Roo.form.TriggerField.superclass.onFocus.call(this);
35821         if(!this.mimicing){
35822             this.wrap.addClass('x-trigger-wrap-focus');
35823             this.mimicing = true;
35824             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35825             if(this.monitorTab){
35826                 this.el.on("keydown", this.checkTab, this);
35827             }
35828         }
35829     },
35830
35831     // private
35832     checkTab : function(e){
35833         if(e.getKey() == e.TAB){
35834             this.triggerBlur();
35835         }
35836     },
35837
35838     // private
35839     onBlur : function(){
35840         // do nothing
35841     },
35842
35843     // private
35844     mimicBlur : function(e, t){
35845         if(!this.wrap.contains(t) && this.validateBlur()){
35846             this.triggerBlur();
35847         }
35848     },
35849
35850     // private
35851     triggerBlur : function(){
35852         this.mimicing = false;
35853         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35854         if(this.monitorTab){
35855             this.el.un("keydown", this.checkTab, this);
35856         }
35857         this.wrap.removeClass('x-trigger-wrap-focus');
35858         Roo.form.TriggerField.superclass.onBlur.call(this);
35859     },
35860
35861     // private
35862     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35863     validateBlur : function(e, t){
35864         return true;
35865     },
35866
35867     // private
35868     onDisable : function(){
35869         Roo.form.TriggerField.superclass.onDisable.call(this);
35870         if(this.wrap){
35871             this.wrap.addClass('x-item-disabled');
35872         }
35873     },
35874
35875     // private
35876     onEnable : function(){
35877         Roo.form.TriggerField.superclass.onEnable.call(this);
35878         if(this.wrap){
35879             this.wrap.removeClass('x-item-disabled');
35880         }
35881     },
35882
35883     // private
35884     onShow : function(){
35885         var ae = this.getActionEl();
35886         
35887         if(ae){
35888             ae.dom.style.display = '';
35889             ae.dom.style.visibility = 'visible';
35890         }
35891     },
35892
35893     // private
35894     
35895     onHide : function(){
35896         var ae = this.getActionEl();
35897         ae.dom.style.display = 'none';
35898     },
35899
35900     /**
35901      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35902      * by an implementing function.
35903      * @method
35904      * @param {EventObject} e
35905      */
35906     onTriggerClick : Roo.emptyFn
35907 });
35908
35909 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35910 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35911 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35912 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35913     initComponent : function(){
35914         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35915
35916         this.triggerConfig = {
35917             tag:'span', cls:'x-form-twin-triggers', cn:[
35918             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35919             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35920         ]};
35921     },
35922
35923     getTrigger : function(index){
35924         return this.triggers[index];
35925     },
35926
35927     initTrigger : function(){
35928         var ts = this.trigger.select('.x-form-trigger', true);
35929         this.wrap.setStyle('overflow', 'hidden');
35930         var triggerField = this;
35931         ts.each(function(t, all, index){
35932             t.hide = function(){
35933                 var w = triggerField.wrap.getWidth();
35934                 this.dom.style.display = 'none';
35935                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35936             };
35937             t.show = function(){
35938                 var w = triggerField.wrap.getWidth();
35939                 this.dom.style.display = '';
35940                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35941             };
35942             var triggerIndex = 'Trigger'+(index+1);
35943
35944             if(this['hide'+triggerIndex]){
35945                 t.dom.style.display = 'none';
35946             }
35947             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
35948             t.addClassOnOver('x-form-trigger-over');
35949             t.addClassOnClick('x-form-trigger-click');
35950         }, this);
35951         this.triggers = ts.elements;
35952     },
35953
35954     onTrigger1Click : Roo.emptyFn,
35955     onTrigger2Click : Roo.emptyFn
35956 });/*
35957  * Based on:
35958  * Ext JS Library 1.1.1
35959  * Copyright(c) 2006-2007, Ext JS, LLC.
35960  *
35961  * Originally Released Under LGPL - original licence link has changed is not relivant.
35962  *
35963  * Fork - LGPL
35964  * <script type="text/javascript">
35965  */
35966  
35967 /**
35968  * @class Roo.form.TextArea
35969  * @extends Roo.form.TextField
35970  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
35971  * support for auto-sizing.
35972  * @constructor
35973  * Creates a new TextArea
35974  * @param {Object} config Configuration options
35975  */
35976 Roo.form.TextArea = function(config){
35977     Roo.form.TextArea.superclass.constructor.call(this, config);
35978     // these are provided exchanges for backwards compat
35979     // minHeight/maxHeight were replaced by growMin/growMax to be
35980     // compatible with TextField growing config values
35981     if(this.minHeight !== undefined){
35982         this.growMin = this.minHeight;
35983     }
35984     if(this.maxHeight !== undefined){
35985         this.growMax = this.maxHeight;
35986     }
35987 };
35988
35989 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
35990     /**
35991      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
35992      */
35993     growMin : 60,
35994     /**
35995      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
35996      */
35997     growMax: 1000,
35998     /**
35999      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36000      * in the field (equivalent to setting overflow: hidden, defaults to false)
36001      */
36002     preventScrollbars: false,
36003     /**
36004      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36005      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36006      */
36007
36008     // private
36009     onRender : function(ct, position){
36010         if(!this.el){
36011             this.defaultAutoCreate = {
36012                 tag: "textarea",
36013                 style:"width:300px;height:60px;",
36014                 autocomplete: "off"
36015             };
36016         }
36017         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36018         if(this.grow){
36019             this.textSizeEl = Roo.DomHelper.append(document.body, {
36020                 tag: "pre", cls: "x-form-grow-sizer"
36021             });
36022             if(this.preventScrollbars){
36023                 this.el.setStyle("overflow", "hidden");
36024             }
36025             this.el.setHeight(this.growMin);
36026         }
36027     },
36028
36029     onDestroy : function(){
36030         if(this.textSizeEl){
36031             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36032         }
36033         Roo.form.TextArea.superclass.onDestroy.call(this);
36034     },
36035
36036     // private
36037     onKeyUp : function(e){
36038         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36039             this.autoSize();
36040         }
36041     },
36042
36043     /**
36044      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36045      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36046      */
36047     autoSize : function(){
36048         if(!this.grow || !this.textSizeEl){
36049             return;
36050         }
36051         var el = this.el;
36052         var v = el.dom.value;
36053         var ts = this.textSizeEl;
36054
36055         ts.innerHTML = '';
36056         ts.appendChild(document.createTextNode(v));
36057         v = ts.innerHTML;
36058
36059         Roo.fly(ts).setWidth(this.el.getWidth());
36060         if(v.length < 1){
36061             v = "&#160;&#160;";
36062         }else{
36063             if(Roo.isIE){
36064                 v = v.replace(/\n/g, '<p>&#160;</p>');
36065             }
36066             v += "&#160;\n&#160;";
36067         }
36068         ts.innerHTML = v;
36069         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36070         if(h != this.lastHeight){
36071             this.lastHeight = h;
36072             this.el.setHeight(h);
36073             this.fireEvent("autosize", this, h);
36074         }
36075     }
36076 });/*
36077  * Based on:
36078  * Ext JS Library 1.1.1
36079  * Copyright(c) 2006-2007, Ext JS, LLC.
36080  *
36081  * Originally Released Under LGPL - original licence link has changed is not relivant.
36082  *
36083  * Fork - LGPL
36084  * <script type="text/javascript">
36085  */
36086  
36087
36088 /**
36089  * @class Roo.form.NumberField
36090  * @extends Roo.form.TextField
36091  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36092  * @constructor
36093  * Creates a new NumberField
36094  * @param {Object} config Configuration options
36095  */
36096 Roo.form.NumberField = function(config){
36097     Roo.form.NumberField.superclass.constructor.call(this, config);
36098 };
36099
36100 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36101     /**
36102      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36103      */
36104     fieldClass: "x-form-field x-form-num-field",
36105     /**
36106      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36107      */
36108     allowDecimals : true,
36109     /**
36110      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36111      */
36112     decimalSeparator : ".",
36113     /**
36114      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36115      */
36116     decimalPrecision : 2,
36117     /**
36118      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36119      */
36120     allowNegative : true,
36121     /**
36122      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36123      */
36124     minValue : Number.NEGATIVE_INFINITY,
36125     /**
36126      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36127      */
36128     maxValue : Number.MAX_VALUE,
36129     /**
36130      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36131      */
36132     minText : "The minimum value for this field is {0}",
36133     /**
36134      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36135      */
36136     maxText : "The maximum value for this field is {0}",
36137     /**
36138      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36139      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36140      */
36141     nanText : "{0} is not a valid number",
36142
36143     // private
36144     initEvents : function(){
36145         Roo.form.NumberField.superclass.initEvents.call(this);
36146         var allowed = "0123456789";
36147         if(this.allowDecimals){
36148             allowed += this.decimalSeparator;
36149         }
36150         if(this.allowNegative){
36151             allowed += "-";
36152         }
36153         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36154         var keyPress = function(e){
36155             var k = e.getKey();
36156             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36157                 return;
36158             }
36159             var c = e.getCharCode();
36160             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36161                 e.stopEvent();
36162             }
36163         };
36164         this.el.on("keypress", keyPress, this);
36165     },
36166
36167     // private
36168     validateValue : function(value){
36169         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36170             return false;
36171         }
36172         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36173              return true;
36174         }
36175         var num = this.parseValue(value);
36176         if(isNaN(num)){
36177             this.markInvalid(String.format(this.nanText, value));
36178             return false;
36179         }
36180         if(num < this.minValue){
36181             this.markInvalid(String.format(this.minText, this.minValue));
36182             return false;
36183         }
36184         if(num > this.maxValue){
36185             this.markInvalid(String.format(this.maxText, this.maxValue));
36186             return false;
36187         }
36188         return true;
36189     },
36190
36191     getValue : function(){
36192         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36193     },
36194
36195     // private
36196     parseValue : function(value){
36197         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36198         return isNaN(value) ? '' : value;
36199     },
36200
36201     // private
36202     fixPrecision : function(value){
36203         var nan = isNaN(value);
36204         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36205             return nan ? '' : value;
36206         }
36207         return parseFloat(value).toFixed(this.decimalPrecision);
36208     },
36209
36210     setValue : function(v){
36211         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36212     },
36213
36214     // private
36215     decimalPrecisionFcn : function(v){
36216         return Math.floor(v);
36217     },
36218
36219     beforeBlur : function(){
36220         var v = this.parseValue(this.getRawValue());
36221         if(v){
36222             this.setValue(this.fixPrecision(v));
36223         }
36224     }
36225 });/*
36226  * Based on:
36227  * Ext JS Library 1.1.1
36228  * Copyright(c) 2006-2007, Ext JS, LLC.
36229  *
36230  * Originally Released Under LGPL - original licence link has changed is not relivant.
36231  *
36232  * Fork - LGPL
36233  * <script type="text/javascript">
36234  */
36235  
36236 /**
36237  * @class Roo.form.DateField
36238  * @extends Roo.form.TriggerField
36239  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36240 * @constructor
36241 * Create a new DateField
36242 * @param {Object} config
36243  */
36244 Roo.form.DateField = function(config){
36245     Roo.form.DateField.superclass.constructor.call(this, config);
36246     
36247       this.addEvents({
36248          
36249         /**
36250          * @event select
36251          * Fires when a date is selected
36252              * @param {Roo.form.DateField} combo This combo box
36253              * @param {Date} date The date selected
36254              */
36255         'select' : true
36256          
36257     });
36258     
36259     
36260     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36261     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36262     this.ddMatch = null;
36263     if(this.disabledDates){
36264         var dd = this.disabledDates;
36265         var re = "(?:";
36266         for(var i = 0; i < dd.length; i++){
36267             re += dd[i];
36268             if(i != dd.length-1) re += "|";
36269         }
36270         this.ddMatch = new RegExp(re + ")");
36271     }
36272 };
36273
36274 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36275     /**
36276      * @cfg {String} format
36277      * The default date format string which can be overriden for localization support.  The format must be
36278      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36279      */
36280     format : "m/d/y",
36281     /**
36282      * @cfg {String} altFormats
36283      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36284      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36285      */
36286     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36287     /**
36288      * @cfg {Array} disabledDays
36289      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36290      */
36291     disabledDays : null,
36292     /**
36293      * @cfg {String} disabledDaysText
36294      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36295      */
36296     disabledDaysText : "Disabled",
36297     /**
36298      * @cfg {Array} disabledDates
36299      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36300      * expression so they are very powerful. Some examples:
36301      * <ul>
36302      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36303      * <li>["03/08", "09/16"] would disable those days for every year</li>
36304      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36305      * <li>["03/../2006"] would disable every day in March 2006</li>
36306      * <li>["^03"] would disable every day in every March</li>
36307      * </ul>
36308      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36309      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36310      */
36311     disabledDates : null,
36312     /**
36313      * @cfg {String} disabledDatesText
36314      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36315      */
36316     disabledDatesText : "Disabled",
36317     /**
36318      * @cfg {Date/String} minValue
36319      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36320      * valid format (defaults to null).
36321      */
36322     minValue : null,
36323     /**
36324      * @cfg {Date/String} maxValue
36325      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36326      * valid format (defaults to null).
36327      */
36328     maxValue : null,
36329     /**
36330      * @cfg {String} minText
36331      * The error text to display when the date in the cell is before minValue (defaults to
36332      * 'The date in this field must be after {minValue}').
36333      */
36334     minText : "The date in this field must be equal to or after {0}",
36335     /**
36336      * @cfg {String} maxText
36337      * The error text to display when the date in the cell is after maxValue (defaults to
36338      * 'The date in this field must be before {maxValue}').
36339      */
36340     maxText : "The date in this field must be equal to or before {0}",
36341     /**
36342      * @cfg {String} invalidText
36343      * The error text to display when the date in the field is invalid (defaults to
36344      * '{value} is not a valid date - it must be in the format {format}').
36345      */
36346     invalidText : "{0} is not a valid date - it must be in the format {1}",
36347     /**
36348      * @cfg {String} triggerClass
36349      * An additional CSS class used to style the trigger button.  The trigger will always get the
36350      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36351      * which displays a calendar icon).
36352      */
36353     triggerClass : 'x-form-date-trigger',
36354     
36355
36356     /**
36357      * @cfg {bool} useIso
36358      * if enabled, then the date field will use a hidden field to store the 
36359      * real value as iso formated date. default (false)
36360      */ 
36361     useIso : false,
36362     /**
36363      * @cfg {String/Object} autoCreate
36364      * A DomHelper element spec, or true for a default element spec (defaults to
36365      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36366      */ 
36367     // private
36368     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36369     
36370     // private
36371     hiddenField: false,
36372     
36373     onRender : function(ct, position)
36374     {
36375         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36376         if (this.useIso) {
36377             this.el.dom.removeAttribute('name'); 
36378             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36379                     'before', true);
36380             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36381             // prevent input submission
36382             this.hiddenName = this.name;
36383         }
36384             
36385             
36386     },
36387     
36388     // private
36389     validateValue : function(value)
36390     {
36391         value = this.formatDate(value);
36392         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36393             return false;
36394         }
36395         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36396              return true;
36397         }
36398         var svalue = value;
36399         value = this.parseDate(value);
36400         if(!value){
36401             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36402             return false;
36403         }
36404         var time = value.getTime();
36405         if(this.minValue && time < this.minValue.getTime()){
36406             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36407             return false;
36408         }
36409         if(this.maxValue && time > this.maxValue.getTime()){
36410             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36411             return false;
36412         }
36413         if(this.disabledDays){
36414             var day = value.getDay();
36415             for(var i = 0; i < this.disabledDays.length; i++) {
36416                 if(day === this.disabledDays[i]){
36417                     this.markInvalid(this.disabledDaysText);
36418                     return false;
36419                 }
36420             }
36421         }
36422         var fvalue = this.formatDate(value);
36423         if(this.ddMatch && this.ddMatch.test(fvalue)){
36424             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36425             return false;
36426         }
36427         return true;
36428     },
36429
36430     // private
36431     // Provides logic to override the default TriggerField.validateBlur which just returns true
36432     validateBlur : function(){
36433         return !this.menu || !this.menu.isVisible();
36434     },
36435
36436     /**
36437      * Returns the current date value of the date field.
36438      * @return {Date} The date value
36439      */
36440     getValue : function(){
36441         
36442         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36443     },
36444
36445     /**
36446      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36447      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36448      * (the default format used is "m/d/y").
36449      * <br />Usage:
36450      * <pre><code>
36451 //All of these calls set the same date value (May 4, 2006)
36452
36453 //Pass a date object:
36454 var dt = new Date('5/4/06');
36455 dateField.setValue(dt);
36456
36457 //Pass a date string (default format):
36458 dateField.setValue('5/4/06');
36459
36460 //Pass a date string (custom format):
36461 dateField.format = 'Y-m-d';
36462 dateField.setValue('2006-5-4');
36463 </code></pre>
36464      * @param {String/Date} date The date or valid date string
36465      */
36466     setValue : function(date){
36467         if (this.hiddenField) {
36468             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36469         }
36470         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36471     },
36472
36473     // private
36474     parseDate : function(value){
36475         if(!value || value instanceof Date){
36476             return value;
36477         }
36478         var v = Date.parseDate(value, this.format);
36479         if(!v && this.altFormats){
36480             if(!this.altFormatsArray){
36481                 this.altFormatsArray = this.altFormats.split("|");
36482             }
36483             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36484                 v = Date.parseDate(value, this.altFormatsArray[i]);
36485             }
36486         }
36487         return v;
36488     },
36489
36490     // private
36491     formatDate : function(date, fmt){
36492         return (!date || !(date instanceof Date)) ?
36493                date : date.dateFormat(fmt || this.format);
36494     },
36495
36496     // private
36497     menuListeners : {
36498         select: function(m, d){
36499             this.setValue(d);
36500             this.fireEvent('select', this, d);
36501         },
36502         show : function(){ // retain focus styling
36503             this.onFocus();
36504         },
36505         hide : function(){
36506             this.focus.defer(10, this);
36507             var ml = this.menuListeners;
36508             this.menu.un("select", ml.select,  this);
36509             this.menu.un("show", ml.show,  this);
36510             this.menu.un("hide", ml.hide,  this);
36511         }
36512     },
36513
36514     // private
36515     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36516     onTriggerClick : function(){
36517         if(this.disabled){
36518             return;
36519         }
36520         if(this.menu == null){
36521             this.menu = new Roo.menu.DateMenu();
36522         }
36523         Roo.apply(this.menu.picker,  {
36524             showClear: this.allowBlank,
36525             minDate : this.minValue,
36526             maxDate : this.maxValue,
36527             disabledDatesRE : this.ddMatch,
36528             disabledDatesText : this.disabledDatesText,
36529             disabledDays : this.disabledDays,
36530             disabledDaysText : this.disabledDaysText,
36531             format : this.format,
36532             minText : String.format(this.minText, this.formatDate(this.minValue)),
36533             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36534         });
36535         this.menu.on(Roo.apply({}, this.menuListeners, {
36536             scope:this
36537         }));
36538         this.menu.picker.setValue(this.getValue() || new Date());
36539         this.menu.show(this.el, "tl-bl?");
36540     },
36541
36542     beforeBlur : function(){
36543         var v = this.parseDate(this.getRawValue());
36544         if(v){
36545             this.setValue(v);
36546         }
36547     }
36548
36549     /** @cfg {Boolean} grow @hide */
36550     /** @cfg {Number} growMin @hide */
36551     /** @cfg {Number} growMax @hide */
36552     /**
36553      * @hide
36554      * @method autoSize
36555      */
36556 });/*
36557  * Based on:
36558  * Ext JS Library 1.1.1
36559  * Copyright(c) 2006-2007, Ext JS, LLC.
36560  *
36561  * Originally Released Under LGPL - original licence link has changed is not relivant.
36562  *
36563  * Fork - LGPL
36564  * <script type="text/javascript">
36565  */
36566  
36567
36568 /**
36569  * @class Roo.form.ComboBox
36570  * @extends Roo.form.TriggerField
36571  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36572  * @constructor
36573  * Create a new ComboBox.
36574  * @param {Object} config Configuration options
36575  */
36576 Roo.form.ComboBox = function(config){
36577     Roo.form.ComboBox.superclass.constructor.call(this, config);
36578     this.addEvents({
36579         /**
36580          * @event expand
36581          * Fires when the dropdown list is expanded
36582              * @param {Roo.form.ComboBox} combo This combo box
36583              */
36584         'expand' : true,
36585         /**
36586          * @event collapse
36587          * Fires when the dropdown list is collapsed
36588              * @param {Roo.form.ComboBox} combo This combo box
36589              */
36590         'collapse' : true,
36591         /**
36592          * @event beforeselect
36593          * Fires before a list item is selected. Return false to cancel the selection.
36594              * @param {Roo.form.ComboBox} combo This combo box
36595              * @param {Roo.data.Record} record The data record returned from the underlying store
36596              * @param {Number} index The index of the selected item in the dropdown list
36597              */
36598         'beforeselect' : true,
36599         /**
36600          * @event select
36601          * Fires when a list item is selected
36602              * @param {Roo.form.ComboBox} combo This combo box
36603              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36604              * @param {Number} index The index of the selected item in the dropdown list
36605              */
36606         'select' : true,
36607         /**
36608          * @event beforequery
36609          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36610          * The event object passed has these properties:
36611              * @param {Roo.form.ComboBox} combo This combo box
36612              * @param {String} query The query
36613              * @param {Boolean} forceAll true to force "all" query
36614              * @param {Boolean} cancel true to cancel the query
36615              * @param {Object} e The query event object
36616              */
36617         'beforequery': true
36618     });
36619     if(this.transform){
36620         this.allowDomMove = false;
36621         var s = Roo.getDom(this.transform);
36622         if(!this.hiddenName){
36623             this.hiddenName = s.name;
36624         }
36625         if(!this.store){
36626             this.mode = 'local';
36627             var d = [], opts = s.options;
36628             for(var i = 0, len = opts.length;i < len; i++){
36629                 var o = opts[i];
36630                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36631                 if(o.selected) {
36632                     this.value = value;
36633                 }
36634                 d.push([value, o.text]);
36635             }
36636             this.store = new Roo.data.SimpleStore({
36637                 'id': 0,
36638                 fields: ['value', 'text'],
36639                 data : d
36640             });
36641             this.valueField = 'value';
36642             this.displayField = 'text';
36643         }
36644         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36645         if(!this.lazyRender){
36646             this.target = true;
36647             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36648             s.parentNode.removeChild(s); // remove it
36649             this.render(this.el.parentNode);
36650         }else{
36651             s.parentNode.removeChild(s); // remove it
36652         }
36653
36654     }
36655     if (this.store) {
36656         this.store = Roo.factory(this.store, Roo.data);
36657     }
36658     
36659     this.selectedIndex = -1;
36660     if(this.mode == 'local'){
36661         if(config.queryDelay === undefined){
36662             this.queryDelay = 10;
36663         }
36664         if(config.minChars === undefined){
36665             this.minChars = 0;
36666         }
36667     }
36668 };
36669
36670 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36671     /**
36672      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36673      */
36674     /**
36675      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36676      * rendering into an Roo.Editor, defaults to false)
36677      */
36678     /**
36679      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36680      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36681      */
36682     /**
36683      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36684      */
36685     /**
36686      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36687      * the dropdown list (defaults to undefined, with no header element)
36688      */
36689
36690      /**
36691      * @cfg {String/Roo.Template} tpl The template to use to render the output
36692      */
36693      
36694     // private
36695     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36696     /**
36697      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36698      */
36699     listWidth: undefined,
36700     /**
36701      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36702      * mode = 'remote' or 'text' if mode = 'local')
36703      */
36704     displayField: undefined,
36705     /**
36706      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36707      * mode = 'remote' or 'value' if mode = 'local'). 
36708      * Note: use of a valueField requires the user make a selection
36709      * in order for a value to be mapped.
36710      */
36711     valueField: undefined,
36712     /**
36713      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36714      * field's data value (defaults to the underlying DOM element's name)
36715      */
36716     hiddenName: undefined,
36717     /**
36718      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36719      */
36720     listClass: '',
36721     /**
36722      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36723      */
36724     selectedClass: 'x-combo-selected',
36725     /**
36726      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36727      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36728      * which displays a downward arrow icon).
36729      */
36730     triggerClass : 'x-form-arrow-trigger',
36731     /**
36732      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36733      */
36734     shadow:'sides',
36735     /**
36736      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36737      * anchor positions (defaults to 'tl-bl')
36738      */
36739     listAlign: 'tl-bl?',
36740     /**
36741      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36742      */
36743     maxHeight: 300,
36744     /**
36745      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36746      * query specified by the allQuery config option (defaults to 'query')
36747      */
36748     triggerAction: 'query',
36749     /**
36750      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36751      * (defaults to 4, does not apply if editable = false)
36752      */
36753     minChars : 4,
36754     /**
36755      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36756      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36757      */
36758     typeAhead: false,
36759     /**
36760      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36761      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36762      */
36763     queryDelay: 500,
36764     /**
36765      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36766      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36767      */
36768     pageSize: 0,
36769     /**
36770      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36771      * when editable = true (defaults to false)
36772      */
36773     selectOnFocus:false,
36774     /**
36775      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36776      */
36777     queryParam: 'query',
36778     /**
36779      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36780      * when mode = 'remote' (defaults to 'Loading...')
36781      */
36782     loadingText: 'Loading...',
36783     /**
36784      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36785      */
36786     resizable: false,
36787     /**
36788      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36789      */
36790     handleHeight : 8,
36791     /**
36792      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36793      * traditional select (defaults to true)
36794      */
36795     editable: true,
36796     /**
36797      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36798      */
36799     allQuery: '',
36800     /**
36801      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36802      */
36803     mode: 'remote',
36804     /**
36805      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36806      * listWidth has a higher value)
36807      */
36808     minListWidth : 70,
36809     /**
36810      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36811      * allow the user to set arbitrary text into the field (defaults to false)
36812      */
36813     forceSelection:false,
36814     /**
36815      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36816      * if typeAhead = true (defaults to 250)
36817      */
36818     typeAheadDelay : 250,
36819     /**
36820      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36821      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36822      */
36823     valueNotFoundText : undefined,
36824     /**
36825      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36826      */
36827     blockFocus : false,
36828     
36829     /**
36830      * @cfg {bool} disableClear Disable showing of clear button.
36831      */
36832     disableClear : false,
36833     
36834     // private
36835     onRender : function(ct, position){
36836         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36837         if(this.hiddenName){
36838             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36839                     'before', true);
36840             this.hiddenField.value =
36841                 this.hiddenValue !== undefined ? this.hiddenValue :
36842                 this.value !== undefined ? this.value : '';
36843
36844             // prevent input submission
36845             this.el.dom.removeAttribute('name');
36846         }
36847         if(Roo.isGecko){
36848             this.el.dom.setAttribute('autocomplete', 'off');
36849         }
36850
36851         var cls = 'x-combo-list';
36852
36853         this.list = new Roo.Layer({
36854             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36855         });
36856
36857         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36858         this.list.setWidth(lw);
36859         this.list.swallowEvent('mousewheel');
36860         this.assetHeight = 0;
36861
36862         if(this.title){
36863             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36864             this.assetHeight += this.header.getHeight();
36865         }
36866
36867         this.innerList = this.list.createChild({cls:cls+'-inner'});
36868         this.innerList.on('mouseover', this.onViewOver, this);
36869         this.innerList.on('mousemove', this.onViewMove, this);
36870         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36871         
36872         if(this.allowBlank && !this.pageSize && !this.disableClear){
36873             this.footer = this.list.createChild({cls:cls+'-ft'});
36874             this.pageTb = new Roo.Toolbar(this.footer);
36875            
36876         }
36877         if(this.pageSize){
36878             this.footer = this.list.createChild({cls:cls+'-ft'});
36879             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36880                     {pageSize: this.pageSize});
36881             
36882         }
36883         
36884         if (this.pageTb && this.allowBlank && !this.disableClear) {
36885             var _this = this;
36886             this.pageTb.add(new Roo.Toolbar.Fill(), {
36887                 cls: 'x-btn-icon x-btn-clear',
36888                 text: '&#160;',
36889                 handler: function()
36890                 {
36891                     _this.collapse();
36892                     _this.clearValue();
36893                     _this.onSelect(false, -1);
36894                 }
36895             });
36896         }
36897         if (this.footer) {
36898             this.assetHeight += this.footer.getHeight();
36899         }
36900         
36901
36902         if(!this.tpl){
36903             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36904         }
36905
36906         this.view = new Roo.View(this.innerList, this.tpl, {
36907             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36908         });
36909
36910         this.view.on('click', this.onViewClick, this);
36911
36912         this.store.on('beforeload', this.onBeforeLoad, this);
36913         this.store.on('load', this.onLoad, this);
36914         this.store.on('loadexception', this.collapse, this);
36915
36916         if(this.resizable){
36917             this.resizer = new Roo.Resizable(this.list,  {
36918                pinned:true, handles:'se'
36919             });
36920             this.resizer.on('resize', function(r, w, h){
36921                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36922                 this.listWidth = w;
36923                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36924                 this.restrictHeight();
36925             }, this);
36926             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36927         }
36928         if(!this.editable){
36929             this.editable = true;
36930             this.setEditable(false);
36931         }
36932     },
36933
36934     // private
36935     initEvents : function(){
36936         Roo.form.ComboBox.superclass.initEvents.call(this);
36937
36938         this.keyNav = new Roo.KeyNav(this.el, {
36939             "up" : function(e){
36940                 this.inKeyMode = true;
36941                 this.selectPrev();
36942             },
36943
36944             "down" : function(e){
36945                 if(!this.isExpanded()){
36946                     this.onTriggerClick();
36947                 }else{
36948                     this.inKeyMode = true;
36949                     this.selectNext();
36950                 }
36951             },
36952
36953             "enter" : function(e){
36954                 this.onViewClick();
36955                 //return true;
36956             },
36957
36958             "esc" : function(e){
36959                 this.collapse();
36960             },
36961
36962             "tab" : function(e){
36963                 this.onViewClick(false);
36964                 return true;
36965             },
36966
36967             scope : this,
36968
36969             doRelay : function(foo, bar, hname){
36970                 if(hname == 'down' || this.scope.isExpanded()){
36971                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
36972                 }
36973                 return true;
36974             },
36975
36976             forceKeyDown: true
36977         });
36978         this.queryDelay = Math.max(this.queryDelay || 10,
36979                 this.mode == 'local' ? 10 : 250);
36980         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
36981         if(this.typeAhead){
36982             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
36983         }
36984         if(this.editable !== false){
36985             this.el.on("keyup", this.onKeyUp, this);
36986         }
36987         if(this.forceSelection){
36988             this.on('blur', this.doForce, this);
36989         }
36990     },
36991
36992     onDestroy : function(){
36993         if(this.view){
36994             this.view.setStore(null);
36995             this.view.el.removeAllListeners();
36996             this.view.el.remove();
36997             this.view.purgeListeners();
36998         }
36999         if(this.list){
37000             this.list.destroy();
37001         }
37002         if(this.store){
37003             this.store.un('beforeload', this.onBeforeLoad, this);
37004             this.store.un('load', this.onLoad, this);
37005             this.store.un('loadexception', this.collapse, this);
37006         }
37007         Roo.form.ComboBox.superclass.onDestroy.call(this);
37008     },
37009
37010     // private
37011     fireKey : function(e){
37012         if(e.isNavKeyPress() && !this.list.isVisible()){
37013             this.fireEvent("specialkey", this, e);
37014         }
37015     },
37016
37017     // private
37018     onResize: function(w, h){
37019         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37020         if(this.list && this.listWidth === undefined){
37021             var lw = Math.max(w, this.minListWidth);
37022             this.list.setWidth(lw);
37023             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37024         }
37025     },
37026
37027     /**
37028      * Allow or prevent the user from directly editing the field text.  If false is passed,
37029      * the user will only be able to select from the items defined in the dropdown list.  This method
37030      * is the runtime equivalent of setting the 'editable' config option at config time.
37031      * @param {Boolean} value True to allow the user to directly edit the field text
37032      */
37033     setEditable : function(value){
37034         if(value == this.editable){
37035             return;
37036         }
37037         this.editable = value;
37038         if(!value){
37039             this.el.dom.setAttribute('readOnly', true);
37040             this.el.on('mousedown', this.onTriggerClick,  this);
37041             this.el.addClass('x-combo-noedit');
37042         }else{
37043             this.el.dom.setAttribute('readOnly', false);
37044             this.el.un('mousedown', this.onTriggerClick,  this);
37045             this.el.removeClass('x-combo-noedit');
37046         }
37047     },
37048
37049     // private
37050     onBeforeLoad : function(){
37051         if(!this.hasFocus){
37052             return;
37053         }
37054         this.innerList.update(this.loadingText ?
37055                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37056         this.restrictHeight();
37057         this.selectedIndex = -1;
37058     },
37059
37060     // private
37061     onLoad : function(){
37062         if(!this.hasFocus){
37063             return;
37064         }
37065         if(this.store.getCount() > 0){
37066             this.expand();
37067             this.restrictHeight();
37068             if(this.lastQuery == this.allQuery){
37069                 if(this.editable){
37070                     this.el.dom.select();
37071                 }
37072                 if(!this.selectByValue(this.value, true)){
37073                     this.select(0, true);
37074                 }
37075             }else{
37076                 this.selectNext();
37077                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37078                     this.taTask.delay(this.typeAheadDelay);
37079                 }
37080             }
37081         }else{
37082             this.onEmptyResults();
37083         }
37084         //this.el.focus();
37085     },
37086
37087     // private
37088     onTypeAhead : function(){
37089         if(this.store.getCount() > 0){
37090             var r = this.store.getAt(0);
37091             var newValue = r.data[this.displayField];
37092             var len = newValue.length;
37093             var selStart = this.getRawValue().length;
37094             if(selStart != len){
37095                 this.setRawValue(newValue);
37096                 this.selectText(selStart, newValue.length);
37097             }
37098         }
37099     },
37100
37101     // private
37102     onSelect : function(record, index){
37103         if(this.fireEvent('beforeselect', this, record, index) !== false){
37104             this.setFromData(index > -1 ? record.data : false);
37105             this.collapse();
37106             this.fireEvent('select', this, record, index);
37107         }
37108     },
37109
37110     /**
37111      * Returns the currently selected field value or empty string if no value is set.
37112      * @return {String} value The selected value
37113      */
37114     getValue : function(){
37115         if(this.valueField){
37116             return typeof this.value != 'undefined' ? this.value : '';
37117         }else{
37118             return Roo.form.ComboBox.superclass.getValue.call(this);
37119         }
37120     },
37121
37122     /**
37123      * Clears any text/value currently set in the field
37124      */
37125     clearValue : function(){
37126         if(this.hiddenField){
37127             this.hiddenField.value = '';
37128         }
37129         this.value = '';
37130         this.setRawValue('');
37131         this.lastSelectionText = '';
37132         this.applyEmptyText();
37133     },
37134
37135     /**
37136      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37137      * will be displayed in the field.  If the value does not match the data value of an existing item,
37138      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37139      * Otherwise the field will be blank (although the value will still be set).
37140      * @param {String} value The value to match
37141      */
37142     setValue : function(v){
37143         var text = v;
37144         if(this.valueField){
37145             var r = this.findRecord(this.valueField, v);
37146             if(r){
37147                 text = r.data[this.displayField];
37148             }else if(this.valueNotFoundText !== undefined){
37149                 text = this.valueNotFoundText;
37150             }
37151         }
37152         this.lastSelectionText = text;
37153         if(this.hiddenField){
37154             this.hiddenField.value = v;
37155         }
37156         Roo.form.ComboBox.superclass.setValue.call(this, text);
37157         this.value = v;
37158     },
37159     /**
37160      * @property {Object} the last set data for the element
37161      */
37162     
37163     lastData : false,
37164     /**
37165      * Sets the value of the field based on a object which is related to the record format for the store.
37166      * @param {Object} value the value to set as. or false on reset?
37167      */
37168     setFromData : function(o){
37169         var dv = ''; // display value
37170         var vv = ''; // value value..
37171         this.lastData = o;
37172         if (this.displayField) {
37173             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37174         } else {
37175             // this is an error condition!!!
37176             console.log('no value field set for '+ this.name);
37177         }
37178         
37179         if(this.valueField){
37180             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37181         }
37182         if(this.hiddenField){
37183             this.hiddenField.value = vv;
37184             
37185             this.lastSelectionText = dv;
37186             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37187             this.value = vv;
37188             return;
37189         }
37190         // no hidden field.. - we store the value in 'value', but still display
37191         // display field!!!!
37192         this.lastSelectionText = dv;
37193         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37194         this.value = vv;
37195         
37196         
37197     },
37198     // private
37199     reset : function(){
37200         // overridden so that last data is reset..
37201         this.setValue(this.originalValue);
37202         this.clearInvalid();
37203         this.lastData = false;
37204     },
37205     // private
37206     findRecord : function(prop, value){
37207         var record;
37208         if(this.store.getCount() > 0){
37209             this.store.each(function(r){
37210                 if(r.data[prop] == value){
37211                     record = r;
37212                     return false;
37213                 }
37214             });
37215         }
37216         return record;
37217     },
37218
37219     // private
37220     onViewMove : function(e, t){
37221         this.inKeyMode = false;
37222     },
37223
37224     // private
37225     onViewOver : function(e, t){
37226         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37227             return;
37228         }
37229         var item = this.view.findItemFromChild(t);
37230         if(item){
37231             var index = this.view.indexOf(item);
37232             this.select(index, false);
37233         }
37234     },
37235
37236     // private
37237     onViewClick : function(doFocus){
37238         var index = this.view.getSelectedIndexes()[0];
37239         var r = this.store.getAt(index);
37240         if(r){
37241             this.onSelect(r, index);
37242         }
37243         if(doFocus !== false && !this.blockFocus){
37244             this.el.focus();
37245         }
37246     },
37247
37248     // private
37249     restrictHeight : function(){
37250         this.innerList.dom.style.height = '';
37251         var inner = this.innerList.dom;
37252         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37253         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37254         this.list.beginUpdate();
37255         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37256         this.list.alignTo(this.el, this.listAlign);
37257         this.list.endUpdate();
37258     },
37259
37260     // private
37261     onEmptyResults : function(){
37262         this.collapse();
37263     },
37264
37265     /**
37266      * Returns true if the dropdown list is expanded, else false.
37267      */
37268     isExpanded : function(){
37269         return this.list.isVisible();
37270     },
37271
37272     /**
37273      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37274      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37275      * @param {String} value The data value of the item to select
37276      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37277      * selected item if it is not currently in view (defaults to true)
37278      * @return {Boolean} True if the value matched an item in the list, else false
37279      */
37280     selectByValue : function(v, scrollIntoView){
37281         if(v !== undefined && v !== null){
37282             var r = this.findRecord(this.valueField || this.displayField, v);
37283             if(r){
37284                 this.select(this.store.indexOf(r), scrollIntoView);
37285                 return true;
37286             }
37287         }
37288         return false;
37289     },
37290
37291     /**
37292      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37293      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37294      * @param {Number} index The zero-based index of the list item to select
37295      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37296      * selected item if it is not currently in view (defaults to true)
37297      */
37298     select : function(index, scrollIntoView){
37299         this.selectedIndex = index;
37300         this.view.select(index);
37301         if(scrollIntoView !== false){
37302             var el = this.view.getNode(index);
37303             if(el){
37304                 this.innerList.scrollChildIntoView(el, false);
37305             }
37306         }
37307     },
37308
37309     // private
37310     selectNext : function(){
37311         var ct = this.store.getCount();
37312         if(ct > 0){
37313             if(this.selectedIndex == -1){
37314                 this.select(0);
37315             }else if(this.selectedIndex < ct-1){
37316                 this.select(this.selectedIndex+1);
37317             }
37318         }
37319     },
37320
37321     // private
37322     selectPrev : function(){
37323         var ct = this.store.getCount();
37324         if(ct > 0){
37325             if(this.selectedIndex == -1){
37326                 this.select(0);
37327             }else if(this.selectedIndex != 0){
37328                 this.select(this.selectedIndex-1);
37329             }
37330         }
37331     },
37332
37333     // private
37334     onKeyUp : function(e){
37335         if(this.editable !== false && !e.isSpecialKey()){
37336             this.lastKey = e.getKey();
37337             this.dqTask.delay(this.queryDelay);
37338         }
37339     },
37340
37341     // private
37342     validateBlur : function(){
37343         return !this.list || !this.list.isVisible();   
37344     },
37345
37346     // private
37347     initQuery : function(){
37348         this.doQuery(this.getRawValue());
37349     },
37350
37351     // private
37352     doForce : function(){
37353         if(this.el.dom.value.length > 0){
37354             this.el.dom.value =
37355                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37356             this.applyEmptyText();
37357         }
37358     },
37359
37360     /**
37361      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37362      * query allowing the query action to be canceled if needed.
37363      * @param {String} query The SQL query to execute
37364      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37365      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37366      * saved in the current store (defaults to false)
37367      */
37368     doQuery : function(q, forceAll){
37369         if(q === undefined || q === null){
37370             q = '';
37371         }
37372         var qe = {
37373             query: q,
37374             forceAll: forceAll,
37375             combo: this,
37376             cancel:false
37377         };
37378         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37379             return false;
37380         }
37381         q = qe.query;
37382         forceAll = qe.forceAll;
37383         if(forceAll === true || (q.length >= this.minChars)){
37384             if(this.lastQuery != q){
37385                 this.lastQuery = q;
37386                 if(this.mode == 'local'){
37387                     this.selectedIndex = -1;
37388                     if(forceAll){
37389                         this.store.clearFilter();
37390                     }else{
37391                         this.store.filter(this.displayField, q);
37392                     }
37393                     this.onLoad();
37394                 }else{
37395                     this.store.baseParams[this.queryParam] = q;
37396                     this.store.load({
37397                         params: this.getParams(q)
37398                     });
37399                     this.expand();
37400                 }
37401             }else{
37402                 this.selectedIndex = -1;
37403                 this.onLoad();   
37404             }
37405         }
37406     },
37407
37408     // private
37409     getParams : function(q){
37410         var p = {};
37411         //p[this.queryParam] = q;
37412         if(this.pageSize){
37413             p.start = 0;
37414             p.limit = this.pageSize;
37415         }
37416         return p;
37417     },
37418
37419     /**
37420      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37421      */
37422     collapse : function(){
37423         if(!this.isExpanded()){
37424             return;
37425         }
37426         this.list.hide();
37427         Roo.get(document).un('mousedown', this.collapseIf, this);
37428         Roo.get(document).un('mousewheel', this.collapseIf, this);
37429         this.fireEvent('collapse', this);
37430     },
37431
37432     // private
37433     collapseIf : function(e){
37434         if(!e.within(this.wrap) && !e.within(this.list)){
37435             this.collapse();
37436         }
37437     },
37438
37439     /**
37440      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37441      */
37442     expand : function(){
37443         if(this.isExpanded() || !this.hasFocus){
37444             return;
37445         }
37446         this.list.alignTo(this.el, this.listAlign);
37447         this.list.show();
37448         Roo.get(document).on('mousedown', this.collapseIf, this);
37449         Roo.get(document).on('mousewheel', this.collapseIf, this);
37450         this.fireEvent('expand', this);
37451     },
37452
37453     // private
37454     // Implements the default empty TriggerField.onTriggerClick function
37455     onTriggerClick : function(){
37456         if(this.disabled){
37457             return;
37458         }
37459         if(this.isExpanded()){
37460             this.collapse();
37461             if (!this.blockFocus) {
37462                 this.el.focus();
37463             }
37464             
37465         }else {
37466             this.hasFocus = true;
37467             if(this.triggerAction == 'all') {
37468                 this.doQuery(this.allQuery, true);
37469             } else {
37470                 this.doQuery(this.getRawValue());
37471             }
37472             if (!this.blockFocus) {
37473                 this.el.focus();
37474             }
37475         }
37476     }
37477
37478     /** 
37479     * @cfg {Boolean} grow 
37480     * @hide 
37481     */
37482     /** 
37483     * @cfg {Number} growMin 
37484     * @hide 
37485     */
37486     /** 
37487     * @cfg {Number} growMax 
37488     * @hide 
37489     */
37490     /**
37491      * @hide
37492      * @method autoSize
37493      */
37494 });/*
37495  * Based on:
37496  * Ext JS Library 1.1.1
37497  * Copyright(c) 2006-2007, Ext JS, LLC.
37498  *
37499  * Originally Released Under LGPL - original licence link has changed is not relivant.
37500  *
37501  * Fork - LGPL
37502  * <script type="text/javascript">
37503  */
37504 /**
37505  * @class Roo.form.Checkbox
37506  * @extends Roo.form.Field
37507  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37508  * @constructor
37509  * Creates a new Checkbox
37510  * @param {Object} config Configuration options
37511  */
37512 Roo.form.Checkbox = function(config){
37513     Roo.form.Checkbox.superclass.constructor.call(this, config);
37514     this.addEvents({
37515         /**
37516          * @event check
37517          * Fires when the checkbox is checked or unchecked.
37518              * @param {Roo.form.Checkbox} this This checkbox
37519              * @param {Boolean} checked The new checked value
37520              */
37521         check : true
37522     });
37523 };
37524
37525 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37526     /**
37527      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37528      */
37529     focusClass : undefined,
37530     /**
37531      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37532      */
37533     fieldClass: "x-form-field",
37534     /**
37535      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37536      */
37537     checked: false,
37538     /**
37539      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37540      * {tag: "input", type: "checkbox", autocomplete: "off"})
37541      */
37542     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37543     /**
37544      * @cfg {String} boxLabel The text that appears beside the checkbox
37545      */
37546     boxLabel : "",
37547     /**
37548      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37549      */  
37550     inputValue : '1',
37551     /**
37552      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37553      */
37554      valueOff: '0', // value when not checked..
37555
37556     actionMode : 'viewEl', 
37557     //
37558     // private
37559     itemCls : 'x-menu-check-item x-form-item',
37560     groupClass : 'x-menu-group-item',
37561     inputType : 'hidden',
37562     
37563     
37564     inSetChecked: false, // check that we are not calling self...
37565     
37566     inputElement: false, // real input element?
37567     basedOn: false, // ????
37568     
37569     isFormField: true, // not sure where this is needed!!!!
37570
37571     onResize : function(){
37572         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37573         if(!this.boxLabel){
37574             this.el.alignTo(this.wrap, 'c-c');
37575         }
37576     },
37577
37578     initEvents : function(){
37579         Roo.form.Checkbox.superclass.initEvents.call(this);
37580         this.el.on("click", this.onClick,  this);
37581         this.el.on("change", this.onClick,  this);
37582     },
37583
37584
37585     getResizeEl : function(){
37586         return this.wrap;
37587     },
37588
37589     getPositionEl : function(){
37590         return this.wrap;
37591     },
37592
37593     // private
37594     onRender : function(ct, position){
37595         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37596         /*
37597         if(this.inputValue !== undefined){
37598             this.el.dom.value = this.inputValue;
37599         }
37600         */
37601         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37602         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37603         var viewEl = this.wrap.createChild({ 
37604             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37605         this.viewEl = viewEl;   
37606         this.wrap.on('click', this.onClick,  this); 
37607         
37608         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37609         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37610         
37611         
37612         
37613         if(this.boxLabel){
37614             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37615         //    viewEl.on('click', this.onClick,  this); 
37616         }
37617         //if(this.checked){
37618             this.setChecked(this.checked);
37619         //}else{
37620             //this.checked = this.el.dom;
37621         //}
37622
37623     },
37624
37625     // private
37626     initValue : Roo.emptyFn,
37627
37628     /**
37629      * Returns the checked state of the checkbox.
37630      * @return {Boolean} True if checked, else false
37631      */
37632     getValue : function(){
37633         if(this.el){
37634             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37635         }
37636         return this.valueOff;
37637         
37638     },
37639
37640         // private
37641     onClick : function(){ 
37642         this.setChecked(!this.checked);
37643
37644         //if(this.el.dom.checked != this.checked){
37645         //    this.setValue(this.el.dom.checked);
37646        // }
37647     },
37648
37649     /**
37650      * Sets the checked state of the checkbox.
37651      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37652      */
37653     setValue : function(v,suppressEvent){
37654         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37655         //if(this.el && this.el.dom){
37656         //    this.el.dom.checked = this.checked;
37657         //    this.el.dom.defaultChecked = this.checked;
37658         //}
37659         this.setChecked(v === this.inputValue);
37660         //this.fireEvent("check", this, this.checked);
37661     },
37662     // private..
37663     setChecked : function(state,suppressEvent)
37664     {
37665         if (this.inSetChecked) {
37666             this.checked = state;
37667             return;
37668         }
37669         
37670     
37671         if(this.wrap){
37672             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37673         }
37674         this.checked = state;
37675         if(suppressEvent !== true){
37676             this.fireEvent('checkchange', this, state);
37677         }
37678         this.inSetChecked = true;
37679         this.el.dom.value = state ? this.inputValue : this.valueOff;
37680         this.inSetChecked = false;
37681         
37682     },
37683     // handle setting of hidden value by some other method!!?!?
37684     setFromHidden: function()
37685     {
37686         if(!this.el){
37687             return;
37688         }
37689         //console.log("SET FROM HIDDEN");
37690         //alert('setFrom hidden');
37691         this.setValue(this.el.dom.value);
37692     },
37693     
37694     onDestroy : function()
37695     {
37696         if(this.viewEl){
37697             Roo.get(this.viewEl).remove();
37698         }
37699          
37700         Roo.form.Checkbox.superclass.onDestroy.call(this);
37701     }
37702
37703 });/*
37704  * Based on:
37705  * Ext JS Library 1.1.1
37706  * Copyright(c) 2006-2007, Ext JS, LLC.
37707  *
37708  * Originally Released Under LGPL - original licence link has changed is not relivant.
37709  *
37710  * Fork - LGPL
37711  * <script type="text/javascript">
37712  */
37713  
37714 /**
37715  * @class Roo.form.Radio
37716  * @extends Roo.form.Checkbox
37717  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37718  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37719  * @constructor
37720  * Creates a new Radio
37721  * @param {Object} config Configuration options
37722  */
37723 Roo.form.Radio = function(){
37724     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37725 };
37726 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37727     inputType: 'radio',
37728
37729     /**
37730      * If this radio is part of a group, it will return the selected value
37731      * @return {String}
37732      */
37733     getGroupValue : function(){
37734         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37735     }
37736 });//<script type="text/javascript">
37737
37738 /*
37739  * Ext JS Library 1.1.1
37740  * Copyright(c) 2006-2007, Ext JS, LLC.
37741  * licensing@extjs.com
37742  * 
37743  * http://www.extjs.com/license
37744  */
37745  
37746  /*
37747   * 
37748   * Known bugs:
37749   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37750   * - IE ? - no idea how much works there.
37751   * 
37752   * 
37753   * 
37754   */
37755  
37756
37757 /**
37758  * @class Ext.form.HtmlEditor
37759  * @extends Ext.form.Field
37760  * Provides a lightweight HTML Editor component.
37761  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37762  * 
37763  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37764  * supported by this editor.</b><br/><br/>
37765  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37766  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37767  */
37768 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37769       /**
37770      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37771      */
37772     toolbars : false,
37773     /**
37774      * @cfg {String} createLinkText The default text for the create link prompt
37775      */
37776     createLinkText : 'Please enter the URL for the link:',
37777     /**
37778      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37779      */
37780     defaultLinkValue : 'http:/'+'/',
37781    
37782     
37783     // id of frame..
37784     frameId: false,
37785     
37786     // private properties
37787     validationEvent : false,
37788     deferHeight: true,
37789     initialized : false,
37790     activated : false,
37791     sourceEditMode : false,
37792     onFocus : Roo.emptyFn,
37793     iframePad:3,
37794     hideMode:'offsets',
37795     defaultAutoCreate : {
37796         tag: "textarea",
37797         style:"width:500px;height:300px;",
37798         autocomplete: "off"
37799     },
37800
37801     // private
37802     initComponent : function(){
37803         this.addEvents({
37804             /**
37805              * @event initialize
37806              * Fires when the editor is fully initialized (including the iframe)
37807              * @param {HtmlEditor} this
37808              */
37809             initialize: true,
37810             /**
37811              * @event activate
37812              * Fires when the editor is first receives the focus. Any insertion must wait
37813              * until after this event.
37814              * @param {HtmlEditor} this
37815              */
37816             activate: true,
37817              /**
37818              * @event beforesync
37819              * Fires before the textarea is updated with content from the editor iframe. Return false
37820              * to cancel the sync.
37821              * @param {HtmlEditor} this
37822              * @param {String} html
37823              */
37824             beforesync: true,
37825              /**
37826              * @event beforepush
37827              * Fires before the iframe editor is updated with content from the textarea. Return false
37828              * to cancel the push.
37829              * @param {HtmlEditor} this
37830              * @param {String} html
37831              */
37832             beforepush: true,
37833              /**
37834              * @event sync
37835              * Fires when the textarea is updated with content from the editor iframe.
37836              * @param {HtmlEditor} this
37837              * @param {String} html
37838              */
37839             sync: true,
37840              /**
37841              * @event push
37842              * Fires when the iframe editor is updated with content from the textarea.
37843              * @param {HtmlEditor} this
37844              * @param {String} html
37845              */
37846             push: true,
37847              /**
37848              * @event editmodechange
37849              * Fires when the editor switches edit modes
37850              * @param {HtmlEditor} this
37851              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37852              */
37853             editmodechange: true,
37854             /**
37855              * @event editorevent
37856              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37857              * @param {HtmlEditor} this
37858              */
37859             editorevent: true
37860         })
37861     },
37862
37863     /**
37864      * Protected method that will not generally be called directly. It
37865      * is called when the editor creates its toolbar. Override this method if you need to
37866      * add custom toolbar buttons.
37867      * @param {HtmlEditor} editor
37868      */
37869     createToolbar : function(editor){
37870         if (!editor.toolbars || !editor.toolbars.length) {
37871             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37872         }
37873         
37874         for (var i =0 ; i < editor.toolbars.length;i++) {
37875             editor.toolbars[i].init(editor);
37876         }
37877          
37878         
37879     },
37880
37881     /**
37882      * Protected method that will not generally be called directly. It
37883      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37884      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37885      */
37886     getDocMarkup : function(){
37887         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37888     },
37889
37890     // private
37891     onRender : function(ct, position){
37892         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37893         this.el.dom.style.border = '0 none';
37894         this.el.dom.setAttribute('tabIndex', -1);
37895         this.el.addClass('x-hidden');
37896         if(Roo.isIE){ // fix IE 1px bogus margin
37897             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37898         }
37899         this.wrap = this.el.wrap({
37900             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37901         });
37902
37903         this.frameId = Roo.id();
37904         this.createToolbar(this);
37905         
37906         
37907         
37908         
37909       
37910         
37911         var iframe = this.wrap.createChild({
37912             tag: 'iframe',
37913             id: this.frameId,
37914             name: this.frameId,
37915             frameBorder : 'no',
37916             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37917         });
37918         
37919        // console.log(iframe);
37920         //this.wrap.dom.appendChild(iframe);
37921
37922         this.iframe = iframe.dom;
37923
37924          this.assignDocWin();
37925         
37926         this.doc.designMode = 'on';
37927        
37928         this.doc.open();
37929         this.doc.write(this.getDocMarkup());
37930         this.doc.close();
37931
37932         
37933         var task = { // must defer to wait for browser to be ready
37934             run : function(){
37935                 //console.log("run task?" + this.doc.readyState);
37936                 this.assignDocWin();
37937                 if(this.doc.body || this.doc.readyState == 'complete'){
37938                     try {
37939                         
37940                        
37941                         this.doc.designMode="on";
37942                     } catch (e) {
37943                         return;
37944                     }
37945                     Roo.TaskMgr.stop(task);
37946                     this.initEditor.defer(10, this);
37947                 }
37948             },
37949             interval : 10,
37950             duration:10000,
37951             scope: this
37952         };
37953         Roo.TaskMgr.start(task);
37954
37955         if(!this.width){
37956             this.setSize(this.el.getSize());
37957         }
37958     },
37959
37960     // private
37961     onResize : function(w, h){
37962         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
37963         if(this.el && this.iframe){
37964             if(typeof w == 'number'){
37965                 var aw = w - this.wrap.getFrameWidth('lr');
37966                 this.el.setWidth(this.adjustWidth('textarea', aw));
37967                 this.iframe.style.width = aw + 'px';
37968             }
37969             if(typeof h == 'number'){
37970                 var tbh = 0;
37971                 for (var i =0; i < this.toolbars.length;i++) {
37972                     // fixme - ask toolbars for heights?
37973                     tbh += this.toolbars[i].tb.el.getHeight();
37974                 }
37975                 
37976                 
37977                 
37978                 
37979                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
37980                 this.el.setHeight(this.adjustWidth('textarea', ah));
37981                 this.iframe.style.height = ah + 'px';
37982                 if(this.doc){
37983                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
37984                 }
37985             }
37986         }
37987     },
37988
37989     /**
37990      * Toggles the editor between standard and source edit mode.
37991      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
37992      */
37993     toggleSourceEdit : function(sourceEditMode){
37994         
37995         this.sourceEditMode = sourceEditMode === true;
37996         
37997         if(this.sourceEditMode){
37998           
37999             this.syncValue();
38000             this.iframe.className = 'x-hidden';
38001             this.el.removeClass('x-hidden');
38002             this.el.dom.removeAttribute('tabIndex');
38003             this.el.focus();
38004         }else{
38005              
38006             this.pushValue();
38007             this.iframe.className = '';
38008             this.el.addClass('x-hidden');
38009             this.el.dom.setAttribute('tabIndex', -1);
38010             this.deferFocus();
38011         }
38012         this.setSize(this.wrap.getSize());
38013         this.fireEvent('editmodechange', this, this.sourceEditMode);
38014     },
38015
38016     // private used internally
38017     createLink : function(){
38018         var url = prompt(this.createLinkText, this.defaultLinkValue);
38019         if(url && url != 'http:/'+'/'){
38020             this.relayCmd('createlink', url);
38021         }
38022     },
38023
38024     // private (for BoxComponent)
38025     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38026
38027     // private (for BoxComponent)
38028     getResizeEl : function(){
38029         return this.wrap;
38030     },
38031
38032     // private (for BoxComponent)
38033     getPositionEl : function(){
38034         return this.wrap;
38035     },
38036
38037     // private
38038     initEvents : function(){
38039         this.originalValue = this.getValue();
38040     },
38041
38042     /**
38043      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38044      * @method
38045      */
38046     markInvalid : Roo.emptyFn,
38047     /**
38048      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38049      * @method
38050      */
38051     clearInvalid : Roo.emptyFn,
38052
38053     setValue : function(v){
38054         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38055         this.pushValue();
38056     },
38057
38058     /**
38059      * Protected method that will not generally be called directly. If you need/want
38060      * custom HTML cleanup, this is the method you should override.
38061      * @param {String} html The HTML to be cleaned
38062      * return {String} The cleaned HTML
38063      */
38064     cleanHtml : function(html){
38065         html = String(html);
38066         if(html.length > 5){
38067             if(Roo.isSafari){ // strip safari nonsense
38068                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38069             }
38070         }
38071         if(html == '&nbsp;'){
38072             html = '';
38073         }
38074         return html;
38075     },
38076
38077     /**
38078      * Protected method that will not generally be called directly. Syncs the contents
38079      * of the editor iframe with the textarea.
38080      */
38081     syncValue : function(){
38082         if(this.initialized){
38083             var bd = (this.doc.body || this.doc.documentElement);
38084             var html = bd.innerHTML;
38085             if(Roo.isSafari){
38086                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38087                 var m = bs.match(/text-align:(.*?);/i);
38088                 if(m && m[1]){
38089                     html = '<div style="'+m[0]+'">' + html + '</div>';
38090                 }
38091             }
38092             html = this.cleanHtml(html);
38093             if(this.fireEvent('beforesync', this, html) !== false){
38094                 this.el.dom.value = html;
38095                 this.fireEvent('sync', this, html);
38096             }
38097         }
38098     },
38099
38100     /**
38101      * Protected method that will not generally be called directly. Pushes the value of the textarea
38102      * into the iframe editor.
38103      */
38104     pushValue : function(){
38105         if(this.initialized){
38106             var v = this.el.dom.value;
38107             if(v.length < 1){
38108                 v = '&#160;';
38109             }
38110             if(this.fireEvent('beforepush', this, v) !== false){
38111                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38112                 this.fireEvent('push', this, v);
38113             }
38114         }
38115     },
38116
38117     // private
38118     deferFocus : function(){
38119         this.focus.defer(10, this);
38120     },
38121
38122     // doc'ed in Field
38123     focus : function(){
38124         if(this.win && !this.sourceEditMode){
38125             this.win.focus();
38126         }else{
38127             this.el.focus();
38128         }
38129     },
38130     
38131     assignDocWin: function()
38132     {
38133         var iframe = this.iframe;
38134         
38135          if(Roo.isIE){
38136             this.doc = iframe.contentWindow.document;
38137             this.win = iframe.contentWindow;
38138         } else {
38139             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38140             this.win = Roo.get(this.frameId).dom.contentWindow;
38141         }
38142     },
38143     
38144     // private
38145     initEditor : function(){
38146         //console.log("INIT EDITOR");
38147         this.assignDocWin();
38148         
38149         
38150         
38151         this.doc.designMode="on";
38152         this.doc.open();
38153         this.doc.write(this.getDocMarkup());
38154         this.doc.close();
38155         
38156         var dbody = (this.doc.body || this.doc.documentElement);
38157         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38158         // this copies styles from the containing element into thsi one..
38159         // not sure why we need all of this..
38160         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38161         ss['background-attachment'] = 'fixed'; // w3c
38162         dbody.bgProperties = 'fixed'; // ie
38163         Roo.DomHelper.applyStyles(dbody, ss);
38164         Roo.EventManager.on(this.doc, {
38165             'mousedown': this.onEditorEvent,
38166             'dblclick': this.onEditorEvent,
38167             'click': this.onEditorEvent,
38168             'keyup': this.onEditorEvent,
38169             buffer:100,
38170             scope: this
38171         });
38172         if(Roo.isGecko){
38173             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38174         }
38175         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38176             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38177         }
38178         this.initialized = true;
38179
38180         this.fireEvent('initialize', this);
38181         this.pushValue();
38182     },
38183
38184     // private
38185     onDestroy : function(){
38186         
38187         
38188         
38189         if(this.rendered){
38190             
38191             for (var i =0; i < this.toolbars.length;i++) {
38192                 // fixme - ask toolbars for heights?
38193                 this.toolbars[i].onDestroy();
38194             }
38195             
38196             this.wrap.dom.innerHTML = '';
38197             this.wrap.remove();
38198         }
38199     },
38200
38201     // private
38202     onFirstFocus : function(){
38203         
38204         this.assignDocWin();
38205         
38206         
38207         this.activated = true;
38208         for (var i =0; i < this.toolbars.length;i++) {
38209             this.toolbars[i].onFirstFocus();
38210         }
38211        
38212         if(Roo.isGecko){ // prevent silly gecko errors
38213             this.win.focus();
38214             var s = this.win.getSelection();
38215             if(!s.focusNode || s.focusNode.nodeType != 3){
38216                 var r = s.getRangeAt(0);
38217                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38218                 r.collapse(true);
38219                 this.deferFocus();
38220             }
38221             try{
38222                 this.execCmd('useCSS', true);
38223                 this.execCmd('styleWithCSS', false);
38224             }catch(e){}
38225         }
38226         this.fireEvent('activate', this);
38227     },
38228
38229     // private
38230     adjustFont: function(btn){
38231         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38232         //if(Roo.isSafari){ // safari
38233         //    adjust *= 2;
38234        // }
38235         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38236         if(Roo.isSafari){ // safari
38237             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38238             v =  (v < 10) ? 10 : v;
38239             v =  (v > 48) ? 48 : v;
38240             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38241             
38242         }
38243         
38244         
38245         v = Math.max(1, v+adjust);
38246         
38247         this.execCmd('FontSize', v  );
38248     },
38249
38250     onEditorEvent : function(e){
38251         this.fireEvent('editorevent', this, e);
38252       //  this.updateToolbar();
38253         this.syncValue();
38254     },
38255
38256     insertTag : function(tg)
38257     {
38258         // could be a bit smarter... -> wrap the current selected tRoo..
38259         
38260         this.execCmd("formatblock",   tg);
38261         
38262     },
38263     
38264     insertText : function(txt)
38265     {
38266         
38267         
38268         range = this.createRange();
38269         range.deleteContents();
38270                //alert(Sender.getAttribute('label'));
38271                
38272         range.insertNode(this.doc.createTextNode(txt));
38273     } ,
38274     
38275     // private
38276     relayBtnCmd : function(btn){
38277         this.relayCmd(btn.cmd);
38278     },
38279
38280     /**
38281      * Executes a Midas editor command on the editor document and performs necessary focus and
38282      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38283      * @param {String} cmd The Midas command
38284      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38285      */
38286     relayCmd : function(cmd, value){
38287         this.win.focus();
38288         this.execCmd(cmd, value);
38289         this.fireEvent('editorevent', this);
38290         //this.updateToolbar();
38291         this.deferFocus();
38292     },
38293
38294     /**
38295      * Executes a Midas editor command directly on the editor document.
38296      * For visual commands, you should use {@link #relayCmd} instead.
38297      * <b>This should only be called after the editor is initialized.</b>
38298      * @param {String} cmd The Midas command
38299      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38300      */
38301     execCmd : function(cmd, value){
38302         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38303         this.syncValue();
38304     },
38305
38306     // private
38307     applyCommand : function(e){
38308         if(e.ctrlKey){
38309             var c = e.getCharCode(), cmd;
38310             if(c > 0){
38311                 c = String.fromCharCode(c);
38312                 switch(c){
38313                     case 'b':
38314                         cmd = 'bold';
38315                     break;
38316                     case 'i':
38317                         cmd = 'italic';
38318                     break;
38319                     case 'u':
38320                         cmd = 'underline';
38321                     break;
38322                 }
38323                 if(cmd){
38324                     this.win.focus();
38325                     this.execCmd(cmd);
38326                     this.deferFocus();
38327                     e.preventDefault();
38328                 }
38329             }
38330         }
38331     },
38332
38333     /**
38334      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38335      * to insert tRoo.
38336      * @param {String} text
38337      */
38338     insertAtCursor : function(text){
38339         if(!this.activated){
38340             return;
38341         }
38342         if(Roo.isIE){
38343             this.win.focus();
38344             var r = this.doc.selection.createRange();
38345             if(r){
38346                 r.collapse(true);
38347                 r.pasteHTML(text);
38348                 this.syncValue();
38349                 this.deferFocus();
38350             }
38351         }else if(Roo.isGecko || Roo.isOpera){
38352             this.win.focus();
38353             this.execCmd('InsertHTML', text);
38354             this.deferFocus();
38355         }else if(Roo.isSafari){
38356             this.execCmd('InsertText', text);
38357             this.deferFocus();
38358         }
38359     },
38360
38361     // private
38362     fixKeys : function(){ // load time branching for fastest keydown performance
38363         if(Roo.isIE){
38364             return function(e){
38365                 var k = e.getKey(), r;
38366                 if(k == e.TAB){
38367                     e.stopEvent();
38368                     r = this.doc.selection.createRange();
38369                     if(r){
38370                         r.collapse(true);
38371                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38372                         this.deferFocus();
38373                     }
38374                 }else if(k == e.ENTER){
38375                     r = this.doc.selection.createRange();
38376                     if(r){
38377                         var target = r.parentElement();
38378                         if(!target || target.tagName.toLowerCase() != 'li'){
38379                             e.stopEvent();
38380                             r.pasteHTML('<br />');
38381                             r.collapse(false);
38382                             r.select();
38383                         }
38384                     }
38385                 }
38386             };
38387         }else if(Roo.isOpera){
38388             return function(e){
38389                 var k = e.getKey();
38390                 if(k == e.TAB){
38391                     e.stopEvent();
38392                     this.win.focus();
38393                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38394                     this.deferFocus();
38395                 }
38396             };
38397         }else if(Roo.isSafari){
38398             return function(e){
38399                 var k = e.getKey();
38400                 if(k == e.TAB){
38401                     e.stopEvent();
38402                     this.execCmd('InsertText','\t');
38403                     this.deferFocus();
38404                 }
38405              };
38406         }
38407     }(),
38408     
38409     getAllAncestors: function()
38410     {
38411         var p = this.getSelectedNode();
38412         var a = [];
38413         if (!p) {
38414             a.push(p); // push blank onto stack..
38415             p = this.getParentElement();
38416         }
38417         
38418         
38419         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38420             a.push(p);
38421             p = p.parentNode;
38422         }
38423         a.push(this.doc.body);
38424         return a;
38425     },
38426     lastSel : false,
38427     lastSelNode : false,
38428     
38429     
38430     getSelection : function() 
38431     {
38432         this.assignDocWin();
38433         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38434     },
38435     
38436     getSelectedNode: function() 
38437     {
38438         // this may only work on Gecko!!!
38439         
38440         // should we cache this!!!!
38441         
38442         
38443         
38444          
38445         var range = this.createRange(this.getSelection());
38446         
38447         if (Roo.isIE) {
38448             var parent = range.parentElement();
38449             while (true) {
38450                 var testRange = range.duplicate();
38451                 testRange.moveToElementText(parent);
38452                 if (testRange.inRange(range)) {
38453                     break;
38454                 }
38455                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38456                     break;
38457                 }
38458                 parent = parent.parentElement;
38459             }
38460             return parent;
38461         }
38462         
38463         
38464         var ar = range.endContainer.childNodes;
38465         if (!ar.length) {
38466             ar = range.commonAncestorContainer.childNodes;
38467             //alert(ar.length);
38468         }
38469         var nodes = [];
38470         var other_nodes = [];
38471         var has_other_nodes = false;
38472         for (var i=0;i<ar.length;i++) {
38473             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38474                 continue;
38475             }
38476             // fullly contained node.
38477             
38478             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38479                 nodes.push(ar[i]);
38480                 continue;
38481             }
38482             
38483             // probably selected..
38484             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38485                 other_nodes.push(ar[i]);
38486                 continue;
38487             }
38488             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38489                 continue;
38490             }
38491             
38492             
38493             has_other_nodes = true;
38494         }
38495         if (!nodes.length && other_nodes.length) {
38496             nodes= other_nodes;
38497         }
38498         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38499             return false;
38500         }
38501         
38502         return nodes[0];
38503     },
38504     createRange: function(sel)
38505     {
38506         // this has strange effects when using with 
38507         // top toolbar - not sure if it's a great idea.
38508         //this.editor.contentWindow.focus();
38509         if (typeof sel != "undefined") {
38510             try {
38511                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38512             } catch(e) {
38513                 return this.doc.createRange();
38514             }
38515         } else {
38516             return this.doc.createRange();
38517         }
38518     },
38519     getParentElement: function()
38520     {
38521         
38522         this.assignDocWin();
38523         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38524         
38525         var range = this.createRange(sel);
38526          
38527         try {
38528             var p = range.commonAncestorContainer;
38529             while (p.nodeType == 3) { // text node
38530                 p = p.parentNode;
38531             }
38532             return p;
38533         } catch (e) {
38534             return null;
38535         }
38536     
38537     },
38538     
38539     
38540     
38541     // BC Hacks - cause I cant work out what i was trying to do..
38542     rangeIntersectsNode : function(range, node)
38543     {
38544         var nodeRange = node.ownerDocument.createRange();
38545         try {
38546             nodeRange.selectNode(node);
38547         }
38548         catch (e) {
38549             nodeRange.selectNodeContents(node);
38550         }
38551
38552         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38553                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38554     },
38555     rangeCompareNode : function(range, node) {
38556         var nodeRange = node.ownerDocument.createRange();
38557         try {
38558             nodeRange.selectNode(node);
38559         } catch (e) {
38560             nodeRange.selectNodeContents(node);
38561         }
38562         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38563         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38564
38565         if (nodeIsBefore && !nodeIsAfter)
38566             return 0;
38567         if (!nodeIsBefore && nodeIsAfter)
38568             return 1;
38569         if (nodeIsBefore && nodeIsAfter)
38570             return 2;
38571
38572         return 3;
38573     }
38574
38575     
38576     
38577     // hide stuff that is not compatible
38578     /**
38579      * @event blur
38580      * @hide
38581      */
38582     /**
38583      * @event change
38584      * @hide
38585      */
38586     /**
38587      * @event focus
38588      * @hide
38589      */
38590     /**
38591      * @event specialkey
38592      * @hide
38593      */
38594     /**
38595      * @cfg {String} fieldClass @hide
38596      */
38597     /**
38598      * @cfg {String} focusClass @hide
38599      */
38600     /**
38601      * @cfg {String} autoCreate @hide
38602      */
38603     /**
38604      * @cfg {String} inputType @hide
38605      */
38606     /**
38607      * @cfg {String} invalidClass @hide
38608      */
38609     /**
38610      * @cfg {String} invalidText @hide
38611      */
38612     /**
38613      * @cfg {String} msgFx @hide
38614      */
38615     /**
38616      * @cfg {String} validateOnBlur @hide
38617      */
38618 });// <script type="text/javascript">
38619 /*
38620  * Based on
38621  * Ext JS Library 1.1.1
38622  * Copyright(c) 2006-2007, Ext JS, LLC.
38623  *  
38624  
38625  */
38626
38627 /**
38628  * @class Roo.form.HtmlEditorToolbar1
38629  * Basic Toolbar
38630  * 
38631  * Usage:
38632  *
38633  new Roo.form.HtmlEditor({
38634     ....
38635     toolbars : [
38636         new Roo.form.HtmlEditorToolbar1({
38637             disable : { fonts: 1 , format: 1, ..., ... , ...],
38638             btns : [ .... ]
38639         })
38640     }
38641      
38642  * 
38643  * @cfg {Object} disable List of elements to disable..
38644  * @cfg {Array} btns List of additional buttons.
38645  * 
38646  * 
38647  * NEEDS Extra CSS? 
38648  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38649  */
38650  
38651 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38652 {
38653     
38654     Roo.apply(this, config);
38655     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38656     // dont call parent... till later.
38657 }
38658
38659 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38660     
38661     tb: false,
38662     
38663     rendered: false,
38664     
38665     editor : false,
38666     /**
38667      * @cfg {Object} disable  List of toolbar elements to disable
38668          
38669      */
38670     disable : false,
38671       /**
38672      * @cfg {Array} fontFamilies An array of available font families
38673      */
38674     fontFamilies : [
38675         'Arial',
38676         'Courier New',
38677         'Tahoma',
38678         'Times New Roman',
38679         'Verdana'
38680     ],
38681     
38682     specialChars : [
38683            "&#169;",
38684           "&#174;",     
38685           "&#8482;",    
38686           "&#163;" ,    
38687          // "&#8212;",    
38688           "&#8230;",    
38689           "&#247;" ,    
38690         //  "&#225;" ,     ?? a acute?
38691            "&#8364;"    , //Euro
38692        //   "&#8220;"    ,
38693         //  "&#8221;"    ,
38694         //  "&#8226;"    ,
38695           "&#176;"  //   , // degrees
38696
38697          // "&#233;"     , // e ecute
38698          // "&#250;"     , // u ecute?
38699     ],
38700     inputElements : [ 
38701             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38702             "input:submit", "input:button", "select", "textarea", "label" ],
38703     formats : [
38704         ["p"] ,  
38705         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38706         ["pre"],[ "code"], 
38707         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38708     ],
38709      /**
38710      * @cfg {String} defaultFont default font to use.
38711      */
38712     defaultFont: 'tahoma',
38713    
38714     fontSelect : false,
38715     
38716     
38717     formatCombo : false,
38718     
38719     init : function(editor)
38720     {
38721         this.editor = editor;
38722         
38723         
38724         var fid = editor.frameId;
38725         var etb = this;
38726         function btn(id, toggle, handler){
38727             var xid = fid + '-'+ id ;
38728             return {
38729                 id : xid,
38730                 cmd : id,
38731                 cls : 'x-btn-icon x-edit-'+id,
38732                 enableToggle:toggle !== false,
38733                 scope: editor, // was editor...
38734                 handler:handler||editor.relayBtnCmd,
38735                 clickEvent:'mousedown',
38736                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38737                 tabIndex:-1
38738             };
38739         }
38740         
38741         
38742         
38743         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38744         this.tb = tb;
38745          // stop form submits
38746         tb.el.on('click', function(e){
38747             e.preventDefault(); // what does this do?
38748         });
38749
38750         if(!this.disable.font && !Roo.isSafari){
38751             /* why no safari for fonts
38752             editor.fontSelect = tb.el.createChild({
38753                 tag:'select',
38754                 tabIndex: -1,
38755                 cls:'x-font-select',
38756                 html: editor.createFontOptions()
38757             });
38758             editor.fontSelect.on('change', function(){
38759                 var font = editor.fontSelect.dom.value;
38760                 editor.relayCmd('fontname', font);
38761                 editor.deferFocus();
38762             }, editor);
38763             tb.add(
38764                 editor.fontSelect.dom,
38765                 '-'
38766             );
38767             */
38768         };
38769         if(!this.disable.formats){
38770             this.formatCombo = new Roo.form.ComboBox({
38771                 store: new Roo.data.SimpleStore({
38772                     id : 'tag',
38773                     fields: ['tag'],
38774                     data : this.formats // from states.js
38775                 }),
38776                 blockFocus : true,
38777                 //autoCreate : {tag: "div",  size: "20"},
38778                 displayField:'tag',
38779                 typeAhead: false,
38780                 mode: 'local',
38781                 editable : false,
38782                 triggerAction: 'all',
38783                 emptyText:'Add tag',
38784                 selectOnFocus:true,
38785                 width:135,
38786                 listeners : {
38787                     'select': function(c, r, i) {
38788                         editor.insertTag(r.get('tag'));
38789                         editor.focus();
38790                     }
38791                 }
38792
38793             });
38794             tb.addField(this.formatCombo);
38795             
38796         }
38797         
38798         if(!this.disable.format){
38799             tb.add(
38800                 btn('bold'),
38801                 btn('italic'),
38802                 btn('underline')
38803             );
38804         };
38805         if(!this.disable.fontSize){
38806             tb.add(
38807                 '-',
38808                 
38809                 
38810                 btn('increasefontsize', false, editor.adjustFont),
38811                 btn('decreasefontsize', false, editor.adjustFont)
38812             );
38813         };
38814         
38815         
38816         if(this.disable.colors){
38817             tb.add(
38818                 '-', {
38819                     id:editor.frameId +'-forecolor',
38820                     cls:'x-btn-icon x-edit-forecolor',
38821                     clickEvent:'mousedown',
38822                     tooltip: this.buttonTips['forecolor'] || undefined,
38823                     tabIndex:-1,
38824                     menu : new Roo.menu.ColorMenu({
38825                         allowReselect: true,
38826                         focus: Roo.emptyFn,
38827                         value:'000000',
38828                         plain:true,
38829                         selectHandler: function(cp, color){
38830                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38831                             editor.deferFocus();
38832                         },
38833                         scope: editor,
38834                         clickEvent:'mousedown'
38835                     })
38836                 }, {
38837                     id:editor.frameId +'backcolor',
38838                     cls:'x-btn-icon x-edit-backcolor',
38839                     clickEvent:'mousedown',
38840                     tooltip: this.buttonTips['backcolor'] || undefined,
38841                     tabIndex:-1,
38842                     menu : new Roo.menu.ColorMenu({
38843                         focus: Roo.emptyFn,
38844                         value:'FFFFFF',
38845                         plain:true,
38846                         allowReselect: true,
38847                         selectHandler: function(cp, color){
38848                             if(Roo.isGecko){
38849                                 editor.execCmd('useCSS', false);
38850                                 editor.execCmd('hilitecolor', color);
38851                                 editor.execCmd('useCSS', true);
38852                                 editor.deferFocus();
38853                             }else{
38854                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38855                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38856                                 editor.deferFocus();
38857                             }
38858                         },
38859                         scope:editor,
38860                         clickEvent:'mousedown'
38861                     })
38862                 }
38863             );
38864         };
38865         // now add all the items...
38866         
38867
38868         if(!this.disable.alignments){
38869             tb.add(
38870                 '-',
38871                 btn('justifyleft'),
38872                 btn('justifycenter'),
38873                 btn('justifyright')
38874             );
38875         };
38876
38877         //if(!Roo.isSafari){
38878             if(!this.disable.links){
38879                 tb.add(
38880                     '-',
38881                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38882                 );
38883             };
38884
38885             if(!this.disable.lists){
38886                 tb.add(
38887                     '-',
38888                     btn('insertorderedlist'),
38889                     btn('insertunorderedlist')
38890                 );
38891             }
38892             if(!this.disable.sourceEdit){
38893                 tb.add(
38894                     '-',
38895                     btn('sourceedit', true, function(btn){
38896                         this.toggleSourceEdit(btn.pressed);
38897                     })
38898                 );
38899             }
38900         //}
38901         
38902         var smenu = { };
38903         // special menu.. - needs to be tidied up..
38904         if (!this.disable.special) {
38905             smenu = {
38906                 text: "&#169;",
38907                 cls: 'x-edit-none',
38908                 menu : {
38909                     items : []
38910                    }
38911             };
38912             for (var i =0; i < this.specialChars.length; i++) {
38913                 smenu.menu.items.push({
38914                     
38915                     text: this.specialChars[i],
38916                     handler: function(a,b) {
38917                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38918                     },
38919                     tabIndex:-1
38920                 });
38921             }
38922             
38923             
38924             tb.add(smenu);
38925             
38926             
38927         }
38928         if (this.btns) {
38929             for(var i =0; i< this.btns.length;i++) {
38930                 var b = this.btns[i];
38931                 b.cls =  'x-edit-none';
38932                 b.scope = editor;
38933                 tb.add(b);
38934             }
38935         
38936         }
38937         
38938         
38939         
38940         // disable everything...
38941         
38942         this.tb.items.each(function(item){
38943            if(item.id != editor.frameId+ '-sourceedit'){
38944                 item.disable();
38945             }
38946         });
38947         this.rendered = true;
38948         
38949         // the all the btns;
38950         editor.on('editorevent', this.updateToolbar, this);
38951         // other toolbars need to implement this..
38952         //editor.on('editmodechange', this.updateToolbar, this);
38953     },
38954     
38955     
38956     
38957     /**
38958      * Protected method that will not generally be called directly. It triggers
38959      * a toolbar update by reading the markup state of the current selection in the editor.
38960      */
38961     updateToolbar: function(){
38962
38963         if(!this.editor.activated){
38964             this.editor.onFirstFocus();
38965             return;
38966         }
38967
38968         var btns = this.tb.items.map, 
38969             doc = this.editor.doc,
38970             frameId = this.editor.frameId;
38971
38972         if(!this.disable.font && !Roo.isSafari){
38973             /*
38974             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
38975             if(name != this.fontSelect.dom.value){
38976                 this.fontSelect.dom.value = name;
38977             }
38978             */
38979         }
38980         if(!this.disable.format){
38981             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
38982             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
38983             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
38984         }
38985         if(!this.disable.alignments){
38986             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
38987             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
38988             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
38989         }
38990         if(!Roo.isSafari && !this.disable.lists){
38991             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
38992             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
38993         }
38994         
38995         var ans = this.editor.getAllAncestors();
38996         if (this.formatCombo) {
38997             
38998             
38999             var store = this.formatCombo.store;
39000             this.formatCombo.setValue("");
39001             for (var i =0; i < ans.length;i++) {
39002                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
39003                     // select it..
39004                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39005                     break;
39006                 }
39007             }
39008         }
39009         
39010         
39011         
39012         // hides menus... - so this cant be on a menu...
39013         Roo.menu.MenuMgr.hideAll();
39014
39015         //this.editorsyncValue();
39016     },
39017    
39018     
39019     createFontOptions : function(){
39020         var buf = [], fs = this.fontFamilies, ff, lc;
39021         for(var i = 0, len = fs.length; i< len; i++){
39022             ff = fs[i];
39023             lc = ff.toLowerCase();
39024             buf.push(
39025                 '<option value="',lc,'" style="font-family:',ff,';"',
39026                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39027                     ff,
39028                 '</option>'
39029             );
39030         }
39031         return buf.join('');
39032     },
39033     
39034     toggleSourceEdit : function(sourceEditMode){
39035         if(sourceEditMode === undefined){
39036             sourceEditMode = !this.sourceEditMode;
39037         }
39038         this.sourceEditMode = sourceEditMode === true;
39039         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39040         // just toggle the button?
39041         if(btn.pressed !== this.editor.sourceEditMode){
39042             btn.toggle(this.editor.sourceEditMode);
39043             return;
39044         }
39045         
39046         if(this.sourceEditMode){
39047             this.tb.items.each(function(item){
39048                 if(item.cmd != 'sourceedit'){
39049                     item.disable();
39050                 }
39051             });
39052           
39053         }else{
39054             if(this.initialized){
39055                 this.tb.items.each(function(item){
39056                     item.enable();
39057                 });
39058             }
39059             
39060         }
39061         // tell the editor that it's been pressed..
39062         this.editor.toggleSourceEdit(sourceEditMode);
39063        
39064     },
39065      /**
39066      * Object collection of toolbar tooltips for the buttons in the editor. The key
39067      * is the command id associated with that button and the value is a valid QuickTips object.
39068      * For example:
39069 <pre><code>
39070 {
39071     bold : {
39072         title: 'Bold (Ctrl+B)',
39073         text: 'Make the selected text bold.',
39074         cls: 'x-html-editor-tip'
39075     },
39076     italic : {
39077         title: 'Italic (Ctrl+I)',
39078         text: 'Make the selected text italic.',
39079         cls: 'x-html-editor-tip'
39080     },
39081     ...
39082 </code></pre>
39083     * @type Object
39084      */
39085     buttonTips : {
39086         bold : {
39087             title: 'Bold (Ctrl+B)',
39088             text: 'Make the selected text bold.',
39089             cls: 'x-html-editor-tip'
39090         },
39091         italic : {
39092             title: 'Italic (Ctrl+I)',
39093             text: 'Make the selected text italic.',
39094             cls: 'x-html-editor-tip'
39095         },
39096         underline : {
39097             title: 'Underline (Ctrl+U)',
39098             text: 'Underline the selected text.',
39099             cls: 'x-html-editor-tip'
39100         },
39101         increasefontsize : {
39102             title: 'Grow Text',
39103             text: 'Increase the font size.',
39104             cls: 'x-html-editor-tip'
39105         },
39106         decreasefontsize : {
39107             title: 'Shrink Text',
39108             text: 'Decrease the font size.',
39109             cls: 'x-html-editor-tip'
39110         },
39111         backcolor : {
39112             title: 'Text Highlight Color',
39113             text: 'Change the background color of the selected text.',
39114             cls: 'x-html-editor-tip'
39115         },
39116         forecolor : {
39117             title: 'Font Color',
39118             text: 'Change the color of the selected text.',
39119             cls: 'x-html-editor-tip'
39120         },
39121         justifyleft : {
39122             title: 'Align Text Left',
39123             text: 'Align text to the left.',
39124             cls: 'x-html-editor-tip'
39125         },
39126         justifycenter : {
39127             title: 'Center Text',
39128             text: 'Center text in the editor.',
39129             cls: 'x-html-editor-tip'
39130         },
39131         justifyright : {
39132             title: 'Align Text Right',
39133             text: 'Align text to the right.',
39134             cls: 'x-html-editor-tip'
39135         },
39136         insertunorderedlist : {
39137             title: 'Bullet List',
39138             text: 'Start a bulleted list.',
39139             cls: 'x-html-editor-tip'
39140         },
39141         insertorderedlist : {
39142             title: 'Numbered List',
39143             text: 'Start a numbered list.',
39144             cls: 'x-html-editor-tip'
39145         },
39146         createlink : {
39147             title: 'Hyperlink',
39148             text: 'Make the selected text a hyperlink.',
39149             cls: 'x-html-editor-tip'
39150         },
39151         sourceedit : {
39152             title: 'Source Edit',
39153             text: 'Switch to source editing mode.',
39154             cls: 'x-html-editor-tip'
39155         }
39156     },
39157     // private
39158     onDestroy : function(){
39159         if(this.rendered){
39160             
39161             this.tb.items.each(function(item){
39162                 if(item.menu){
39163                     item.menu.removeAll();
39164                     if(item.menu.el){
39165                         item.menu.el.destroy();
39166                     }
39167                 }
39168                 item.destroy();
39169             });
39170              
39171         }
39172     },
39173     onFirstFocus: function() {
39174         this.tb.items.each(function(item){
39175            item.enable();
39176         });
39177     }
39178 });
39179
39180
39181
39182
39183 // <script type="text/javascript">
39184 /*
39185  * Based on
39186  * Ext JS Library 1.1.1
39187  * Copyright(c) 2006-2007, Ext JS, LLC.
39188  *  
39189  
39190  */
39191
39192  
39193 /**
39194  * @class Roo.form.HtmlEditor.ToolbarContext
39195  * Context Toolbar
39196  * 
39197  * Usage:
39198  *
39199  new Roo.form.HtmlEditor({
39200     ....
39201     toolbars : [
39202         new Roo.form.HtmlEditor.ToolbarStandard(),
39203         new Roo.form.HtmlEditor.ToolbarContext()
39204         })
39205     }
39206      
39207  * 
39208  * @config : {Object} disable List of elements to disable.. (not done yet.)
39209  * 
39210  * 
39211  */
39212
39213 Roo.form.HtmlEditor.ToolbarContext = function(config)
39214 {
39215     
39216     Roo.apply(this, config);
39217     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39218     // dont call parent... till later.
39219 }
39220 Roo.form.HtmlEditor.ToolbarContext.types = {
39221     'IMG' : {
39222         width : {
39223             title: "Width",
39224             width: 40
39225         },
39226         height:  {
39227             title: "Height",
39228             width: 40
39229         },
39230         align: {
39231             title: "Align",
39232             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39233             width : 80
39234             
39235         },
39236         border: {
39237             title: "Border",
39238             width: 40
39239         },
39240         alt: {
39241             title: "Alt",
39242             width: 120
39243         },
39244         src : {
39245             title: "Src",
39246             width: 220
39247         }
39248         
39249     },
39250     'A' : {
39251         name : {
39252             title: "Name",
39253             width: 50
39254         },
39255         href:  {
39256             title: "Href",
39257             width: 220
39258         } // border?
39259         
39260     },
39261     'TABLE' : {
39262         rows : {
39263             title: "Rows",
39264             width: 20
39265         },
39266         cols : {
39267             title: "Cols",
39268             width: 20
39269         },
39270         width : {
39271             title: "Width",
39272             width: 40
39273         },
39274         height : {
39275             title: "Height",
39276             width: 40
39277         },
39278         border : {
39279             title: "Border",
39280             width: 20
39281         }
39282     },
39283     'TD' : {
39284         width : {
39285             title: "Width",
39286             width: 40
39287         },
39288         height : {
39289             title: "Height",
39290             width: 40
39291         },   
39292         align: {
39293             title: "Align",
39294             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39295             width: 40
39296         },
39297         valign: {
39298             title: "Valign",
39299             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39300             width: 40
39301         },
39302         colspan: {
39303             title: "Colspan",
39304             width: 20
39305             
39306         }
39307     },
39308     'INPUT' : {
39309         name : {
39310             title: "name",
39311             width: 120
39312         },
39313         value : {
39314             title: "Value",
39315             width: 120
39316         },
39317         width : {
39318             title: "Width",
39319             width: 40
39320         }
39321     },
39322     'LABEL' : {
39323         'for' : {
39324             title: "For",
39325             width: 120
39326         }
39327     },
39328     'TEXTAREA' : {
39329           name : {
39330             title: "name",
39331             width: 120
39332         },
39333         rows : {
39334             title: "Rows",
39335             width: 20
39336         },
39337         cols : {
39338             title: "Cols",
39339             width: 20
39340         }
39341     },
39342     'SELECT' : {
39343         name : {
39344             title: "name",
39345             width: 120
39346         },
39347         selectoptions : {
39348             title: "Options",
39349             width: 200
39350         }
39351     },
39352     'BODY' : {
39353         title : {
39354             title: "title",
39355             width: 120,
39356             disabled : true
39357         }
39358     }
39359 };
39360
39361
39362
39363 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39364     
39365     tb: false,
39366     
39367     rendered: false,
39368     
39369     editor : false,
39370     /**
39371      * @cfg {Object} disable  List of toolbar elements to disable
39372          
39373      */
39374     disable : false,
39375     
39376     
39377     
39378     toolbars : false,
39379     
39380     init : function(editor)
39381     {
39382         this.editor = editor;
39383         
39384         
39385         var fid = editor.frameId;
39386         var etb = this;
39387         function btn(id, toggle, handler){
39388             var xid = fid + '-'+ id ;
39389             return {
39390                 id : xid,
39391                 cmd : id,
39392                 cls : 'x-btn-icon x-edit-'+id,
39393                 enableToggle:toggle !== false,
39394                 scope: editor, // was editor...
39395                 handler:handler||editor.relayBtnCmd,
39396                 clickEvent:'mousedown',
39397                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39398                 tabIndex:-1
39399             };
39400         }
39401         // create a new element.
39402         var wdiv = editor.wrap.createChild({
39403                 tag: 'div'
39404             }, editor.wrap.dom.firstChild.nextSibling, true);
39405         
39406         // can we do this more than once??
39407         
39408          // stop form submits
39409       
39410  
39411         // disable everything...
39412         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39413         this.toolbars = {};
39414            
39415         for (var i in  ty) {
39416             this.toolbars[i] = this.buildToolbar(ty[i],i);
39417         }
39418         this.tb = this.toolbars.BODY;
39419         this.tb.el.show();
39420         
39421          
39422         this.rendered = true;
39423         
39424         // the all the btns;
39425         editor.on('editorevent', this.updateToolbar, this);
39426         // other toolbars need to implement this..
39427         //editor.on('editmodechange', this.updateToolbar, this);
39428     },
39429     
39430     
39431     
39432     /**
39433      * Protected method that will not generally be called directly. It triggers
39434      * a toolbar update by reading the markup state of the current selection in the editor.
39435      */
39436     updateToolbar: function(){
39437
39438         if(!this.editor.activated){
39439             this.editor.onFirstFocus();
39440             return;
39441         }
39442
39443         
39444         var ans = this.editor.getAllAncestors();
39445         
39446         // pick
39447         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39448         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39449         sel = sel ? sel : this.editor.doc.body;
39450         sel = sel.tagName.length ? sel : this.editor.doc.body;
39451         var tn = sel.tagName.toUpperCase();
39452         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39453         tn = sel.tagName.toUpperCase();
39454         if (this.tb.name  == tn) {
39455             return; // no change
39456         }
39457         this.tb.el.hide();
39458         ///console.log("show: " + tn);
39459         this.tb =  this.toolbars[tn];
39460         this.tb.el.show();
39461         this.tb.fields.each(function(e) {
39462             e.setValue(sel.getAttribute(e.name));
39463         });
39464         this.tb.selectedNode = sel;
39465         
39466         
39467         Roo.menu.MenuMgr.hideAll();
39468
39469         //this.editorsyncValue();
39470     },
39471    
39472        
39473     // private
39474     onDestroy : function(){
39475         if(this.rendered){
39476             
39477             this.tb.items.each(function(item){
39478                 if(item.menu){
39479                     item.menu.removeAll();
39480                     if(item.menu.el){
39481                         item.menu.el.destroy();
39482                     }
39483                 }
39484                 item.destroy();
39485             });
39486              
39487         }
39488     },
39489     onFirstFocus: function() {
39490         // need to do this for all the toolbars..
39491         this.tb.items.each(function(item){
39492            item.enable();
39493         });
39494     },
39495     buildToolbar: function(tlist, nm)
39496     {
39497         var editor = this.editor;
39498          // create a new element.
39499         var wdiv = editor.wrap.createChild({
39500                 tag: 'div'
39501             }, editor.wrap.dom.firstChild.nextSibling, true);
39502         
39503        
39504         var tb = new Roo.Toolbar(wdiv);
39505         tb.add(nm+ ":&nbsp;");
39506         for (var i in tlist) {
39507             var item = tlist[i];
39508             tb.add(item.title + ":&nbsp;");
39509             if (item.opts) {
39510                 // fixme
39511                 
39512               
39513                 tb.addField( new Roo.form.ComboBox({
39514                     store: new Roo.data.SimpleStore({
39515                         id : 'val',
39516                         fields: ['val'],
39517                         data : item.opts // from states.js
39518                     }),
39519                     name : i,
39520                     displayField:'val',
39521                     typeAhead: false,
39522                     mode: 'local',
39523                     editable : false,
39524                     triggerAction: 'all',
39525                     emptyText:'Select',
39526                     selectOnFocus:true,
39527                     width: item.width ? item.width  : 130,
39528                     listeners : {
39529                         'select': function(c, r, i) {
39530                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39531                         }
39532                     }
39533
39534                 }));
39535                 continue;
39536                     
39537                 
39538                 
39539                 
39540                 
39541                 tb.addField( new Roo.form.TextField({
39542                     name: i,
39543                     width: 100,
39544                     //allowBlank:false,
39545                     value: ''
39546                 }));
39547                 continue;
39548             }
39549             tb.addField( new Roo.form.TextField({
39550                 name: i,
39551                 width: item.width,
39552                 //allowBlank:true,
39553                 value: '',
39554                 listeners: {
39555                     'change' : function(f, nv, ov) {
39556                         tb.selectedNode.setAttribute(f.name, nv);
39557                     }
39558                 }
39559             }));
39560              
39561         }
39562         tb.el.on('click', function(e){
39563             e.preventDefault(); // what does this do?
39564         });
39565         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39566         tb.el.hide();
39567         tb.name = nm;
39568         // dont need to disable them... as they will get hidden
39569         return tb;
39570          
39571         
39572     }
39573     
39574     
39575     
39576     
39577 });
39578
39579
39580
39581
39582
39583 /*
39584  * Based on:
39585  * Ext JS Library 1.1.1
39586  * Copyright(c) 2006-2007, Ext JS, LLC.
39587  *
39588  * Originally Released Under LGPL - original licence link has changed is not relivant.
39589  *
39590  * Fork - LGPL
39591  * <script type="text/javascript">
39592  */
39593  
39594 /**
39595  * @class Roo.form.BasicForm
39596  * @extends Roo.util.Observable
39597  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39598  * @constructor
39599  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39600  * @param {Object} config Configuration options
39601  */
39602 Roo.form.BasicForm = function(el, config){
39603     Roo.apply(this, config);
39604     /*
39605      * The Roo.form.Field items in this form.
39606      * @type MixedCollection
39607      */
39608     this.items = new Roo.util.MixedCollection(false, function(o){
39609         return o.id || (o.id = Roo.id());
39610     });
39611     this.addEvents({
39612         /**
39613          * @event beforeaction
39614          * Fires before any action is performed. Return false to cancel the action.
39615          * @param {Form} this
39616          * @param {Action} action The action to be performed
39617          */
39618         beforeaction: true,
39619         /**
39620          * @event actionfailed
39621          * Fires when an action fails.
39622          * @param {Form} this
39623          * @param {Action} action The action that failed
39624          */
39625         actionfailed : true,
39626         /**
39627          * @event actioncomplete
39628          * Fires when an action is completed.
39629          * @param {Form} this
39630          * @param {Action} action The action that completed
39631          */
39632         actioncomplete : true
39633     });
39634     if(el){
39635         this.initEl(el);
39636     }
39637     Roo.form.BasicForm.superclass.constructor.call(this);
39638 };
39639
39640 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39641     /**
39642      * @cfg {String} method
39643      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39644      */
39645     /**
39646      * @cfg {DataReader} reader
39647      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39648      * This is optional as there is built-in support for processing JSON.
39649      */
39650     /**
39651      * @cfg {DataReader} errorReader
39652      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39653      * This is completely optional as there is built-in support for processing JSON.
39654      */
39655     /**
39656      * @cfg {String} url
39657      * The URL to use for form actions if one isn't supplied in the action options.
39658      */
39659     /**
39660      * @cfg {Boolean} fileUpload
39661      * Set to true if this form is a file upload.
39662      */
39663     /**
39664      * @cfg {Object} baseParams
39665      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39666      */
39667     /**
39668      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39669      */
39670     timeout: 30,
39671
39672     // private
39673     activeAction : null,
39674
39675     /**
39676      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39677      * or setValues() data instead of when the form was first created.
39678      */
39679     trackResetOnLoad : false,
39680
39681     /**
39682      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39683      * element by passing it or its id or mask the form itself by passing in true.
39684      * @type Mixed
39685      */
39686     waitMsgTarget : undefined,
39687
39688     // private
39689     initEl : function(el){
39690         this.el = Roo.get(el);
39691         this.id = this.el.id || Roo.id();
39692         this.el.on('submit', this.onSubmit, this);
39693         this.el.addClass('x-form');
39694     },
39695
39696     // private
39697     onSubmit : function(e){
39698         e.stopEvent();
39699     },
39700
39701     /**
39702      * Returns true if client-side validation on the form is successful.
39703      * @return Boolean
39704      */
39705     isValid : function(){
39706         var valid = true;
39707         this.items.each(function(f){
39708            if(!f.validate()){
39709                valid = false;
39710            }
39711         });
39712         return valid;
39713     },
39714
39715     /**
39716      * Returns true if any fields in this form have changed since their original load.
39717      * @return Boolean
39718      */
39719     isDirty : function(){
39720         var dirty = false;
39721         this.items.each(function(f){
39722            if(f.isDirty()){
39723                dirty = true;
39724                return false;
39725            }
39726         });
39727         return dirty;
39728     },
39729
39730     /**
39731      * Performs a predefined action (submit or load) or custom actions you define on this form.
39732      * @param {String} actionName The name of the action type
39733      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39734      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39735      * accept other config options):
39736      * <pre>
39737 Property          Type             Description
39738 ----------------  ---------------  ----------------------------------------------------------------------------------
39739 url               String           The url for the action (defaults to the form's url)
39740 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39741 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39742 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39743                                    validate the form on the client (defaults to false)
39744      * </pre>
39745      * @return {BasicForm} this
39746      */
39747     doAction : function(action, options){
39748         if(typeof action == 'string'){
39749             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39750         }
39751         if(this.fireEvent('beforeaction', this, action) !== false){
39752             this.beforeAction(action);
39753             action.run.defer(100, action);
39754         }
39755         return this;
39756     },
39757
39758     /**
39759      * Shortcut to do a submit action.
39760      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39761      * @return {BasicForm} this
39762      */
39763     submit : function(options){
39764         this.doAction('submit', options);
39765         return this;
39766     },
39767
39768     /**
39769      * Shortcut to do a load action.
39770      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39771      * @return {BasicForm} this
39772      */
39773     load : function(options){
39774         this.doAction('load', options);
39775         return this;
39776     },
39777
39778     /**
39779      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39780      * @param {Record} record The record to edit
39781      * @return {BasicForm} this
39782      */
39783     updateRecord : function(record){
39784         record.beginEdit();
39785         var fs = record.fields;
39786         fs.each(function(f){
39787             var field = this.findField(f.name);
39788             if(field){
39789                 record.set(f.name, field.getValue());
39790             }
39791         }, this);
39792         record.endEdit();
39793         return this;
39794     },
39795
39796     /**
39797      * Loads an Roo.data.Record into this form.
39798      * @param {Record} record The record to load
39799      * @return {BasicForm} this
39800      */
39801     loadRecord : function(record){
39802         this.setValues(record.data);
39803         return this;
39804     },
39805
39806     // private
39807     beforeAction : function(action){
39808         var o = action.options;
39809         if(o.waitMsg){
39810             if(this.waitMsgTarget === true){
39811                 this.el.mask(o.waitMsg, 'x-mask-loading');
39812             }else if(this.waitMsgTarget){
39813                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39814                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39815             }else{
39816                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39817             }
39818         }
39819     },
39820
39821     // private
39822     afterAction : function(action, success){
39823         this.activeAction = null;
39824         var o = action.options;
39825         if(o.waitMsg){
39826             if(this.waitMsgTarget === true){
39827                 this.el.unmask();
39828             }else if(this.waitMsgTarget){
39829                 this.waitMsgTarget.unmask();
39830             }else{
39831                 Roo.MessageBox.updateProgress(1);
39832                 Roo.MessageBox.hide();
39833             }
39834         }
39835         if(success){
39836             if(o.reset){
39837                 this.reset();
39838             }
39839             Roo.callback(o.success, o.scope, [this, action]);
39840             this.fireEvent('actioncomplete', this, action);
39841         }else{
39842             Roo.callback(o.failure, o.scope, [this, action]);
39843             this.fireEvent('actionfailed', this, action);
39844         }
39845     },
39846
39847     /**
39848      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
39849      * @param {String} id The value to search for
39850      * @return Field
39851      */
39852     findField : function(id){
39853         var field = this.items.get(id);
39854         if(!field){
39855             this.items.each(function(f){
39856                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
39857                     field = f;
39858                     return false;
39859                 }
39860             });
39861         }
39862         return field || null;
39863     },
39864
39865
39866     /**
39867      * Mark fields in this form invalid in bulk.
39868      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
39869      * @return {BasicForm} this
39870      */
39871     markInvalid : function(errors){
39872         if(errors instanceof Array){
39873             for(var i = 0, len = errors.length; i < len; i++){
39874                 var fieldError = errors[i];
39875                 var f = this.findField(fieldError.id);
39876                 if(f){
39877                     f.markInvalid(fieldError.msg);
39878                 }
39879             }
39880         }else{
39881             var field, id;
39882             for(id in errors){
39883                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
39884                     field.markInvalid(errors[id]);
39885                 }
39886             }
39887         }
39888         return this;
39889     },
39890
39891     /**
39892      * Set values for fields in this form in bulk.
39893      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
39894      * @return {BasicForm} this
39895      */
39896     setValues : function(values){
39897         if(values instanceof Array){ // array of objects
39898             for(var i = 0, len = values.length; i < len; i++){
39899                 var v = values[i];
39900                 var f = this.findField(v.id);
39901                 if(f){
39902                     f.setValue(v.value);
39903                     if(this.trackResetOnLoad){
39904                         f.originalValue = f.getValue();
39905                     }
39906                 }
39907             }
39908         }else{ // object hash
39909             var field, id;
39910             for(id in values){
39911                 if(typeof values[id] != 'function' && (field = this.findField(id))){
39912                     
39913                     if (field.setFromData && 
39914                         field.valueField && 
39915                         field.displayField &&
39916                         // combos' with local stores can 
39917                         // be queried via setValue()
39918                         // to set their value..
39919                         (field.store && !field.store.isLocal)
39920                         ) {
39921                         // it's a combo
39922                         var sd = { };
39923                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
39924                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
39925                         field.setFromData(sd);
39926                         
39927                     } else {
39928                         field.setValue(values[id]);
39929                     }
39930                     
39931                     
39932                     if(this.trackResetOnLoad){
39933                         field.originalValue = field.getValue();
39934                     }
39935                 }
39936             }
39937         }
39938         return this;
39939     },
39940
39941     /**
39942      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
39943      * they are returned as an array.
39944      * @param {Boolean} asString
39945      * @return {Object}
39946      */
39947     getValues : function(asString){
39948         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
39949         if(asString === true){
39950             return fs;
39951         }
39952         return Roo.urlDecode(fs);
39953     },
39954
39955     /**
39956      * Clears all invalid messages in this form.
39957      * @return {BasicForm} this
39958      */
39959     clearInvalid : function(){
39960         this.items.each(function(f){
39961            f.clearInvalid();
39962         });
39963         return this;
39964     },
39965
39966     /**
39967      * Resets this form.
39968      * @return {BasicForm} this
39969      */
39970     reset : function(){
39971         this.items.each(function(f){
39972             f.reset();
39973         });
39974         return this;
39975     },
39976
39977     /**
39978      * Add Roo.form components to this form.
39979      * @param {Field} field1
39980      * @param {Field} field2 (optional)
39981      * @param {Field} etc (optional)
39982      * @return {BasicForm} this
39983      */
39984     add : function(){
39985         this.items.addAll(Array.prototype.slice.call(arguments, 0));
39986         return this;
39987     },
39988
39989
39990     /**
39991      * Removes a field from the items collection (does NOT remove its markup).
39992      * @param {Field} field
39993      * @return {BasicForm} this
39994      */
39995     remove : function(field){
39996         this.items.remove(field);
39997         return this;
39998     },
39999
40000     /**
40001      * Looks at the fields in this form, checks them for an id attribute,
40002      * and calls applyTo on the existing dom element with that id.
40003      * @return {BasicForm} this
40004      */
40005     render : function(){
40006         this.items.each(function(f){
40007             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40008                 f.applyTo(f.id);
40009             }
40010         });
40011         return this;
40012     },
40013
40014     /**
40015      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40016      * @param {Object} values
40017      * @return {BasicForm} this
40018      */
40019     applyToFields : function(o){
40020         this.items.each(function(f){
40021            Roo.apply(f, o);
40022         });
40023         return this;
40024     },
40025
40026     /**
40027      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40028      * @param {Object} values
40029      * @return {BasicForm} this
40030      */
40031     applyIfToFields : function(o){
40032         this.items.each(function(f){
40033            Roo.applyIf(f, o);
40034         });
40035         return this;
40036     }
40037 });
40038
40039 // back compat
40040 Roo.BasicForm = Roo.form.BasicForm;/*
40041  * Based on:
40042  * Ext JS Library 1.1.1
40043  * Copyright(c) 2006-2007, Ext JS, LLC.
40044  *
40045  * Originally Released Under LGPL - original licence link has changed is not relivant.
40046  *
40047  * Fork - LGPL
40048  * <script type="text/javascript">
40049  */
40050
40051 /**
40052  * @class Roo.form.Form
40053  * @extends Roo.form.BasicForm
40054  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40055  * @constructor
40056  * @param {Object} config Configuration options
40057  */
40058 Roo.form.Form = function(config){
40059     var xitems =  [];
40060     if (config.items) {
40061         xitems = config.items;
40062         delete config.items;
40063     }
40064     
40065     
40066     Roo.form.Form.superclass.constructor.call(this, null, config);
40067     this.url = this.url || this.action;
40068     if(!this.root){
40069         this.root = new Roo.form.Layout(Roo.applyIf({
40070             id: Roo.id()
40071         }, config));
40072     }
40073     this.active = this.root;
40074     /**
40075      * Array of all the buttons that have been added to this form via {@link addButton}
40076      * @type Array
40077      */
40078     this.buttons = [];
40079     this.allItems = [];
40080     this.addEvents({
40081         /**
40082          * @event clientvalidation
40083          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40084          * @param {Form} this
40085          * @param {Boolean} valid true if the form has passed client-side validation
40086          */
40087         clientvalidation: true,
40088         /**
40089          * @event rendered
40090          * Fires when the form is rendered
40091          * @param {Roo.form.Form} form
40092          */
40093         rendered : true
40094     });
40095     
40096     Roo.each(xitems, this.addxtype, this);
40097     
40098     
40099     
40100 };
40101
40102 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40103     /**
40104      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40105      */
40106     /**
40107      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40108      */
40109     /**
40110      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40111      */
40112     buttonAlign:'center',
40113
40114     /**
40115      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40116      */
40117     minButtonWidth:75,
40118
40119     /**
40120      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40121      * This property cascades to child containers if not set.
40122      */
40123     labelAlign:'left',
40124
40125     /**
40126      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40127      * fires a looping event with that state. This is required to bind buttons to the valid
40128      * state using the config value formBind:true on the button.
40129      */
40130     monitorValid : false,
40131
40132     /**
40133      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40134      */
40135     monitorPoll : 200,
40136
40137     /**
40138      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40139      * fields are added and the column is closed. If no fields are passed the column remains open
40140      * until end() is called.
40141      * @param {Object} config The config to pass to the column
40142      * @param {Field} field1 (optional)
40143      * @param {Field} field2 (optional)
40144      * @param {Field} etc (optional)
40145      * @return Column The column container object
40146      */
40147     column : function(c){
40148         var col = new Roo.form.Column(c);
40149         this.start(col);
40150         if(arguments.length > 1){ // duplicate code required because of Opera
40151             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40152             this.end();
40153         }
40154         return col;
40155     },
40156
40157     /**
40158      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40159      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40160      * until end() is called.
40161      * @param {Object} config The config to pass to the fieldset
40162      * @param {Field} field1 (optional)
40163      * @param {Field} field2 (optional)
40164      * @param {Field} etc (optional)
40165      * @return FieldSet The fieldset container object
40166      */
40167     fieldset : function(c){
40168         var fs = new Roo.form.FieldSet(c);
40169         this.start(fs);
40170         if(arguments.length > 1){ // duplicate code required because of Opera
40171             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40172             this.end();
40173         }
40174         return fs;
40175     },
40176
40177     /**
40178      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40179      * fields are added and the container is closed. If no fields are passed the container remains open
40180      * until end() is called.
40181      * @param {Object} config The config to pass to the Layout
40182      * @param {Field} field1 (optional)
40183      * @param {Field} field2 (optional)
40184      * @param {Field} etc (optional)
40185      * @return Layout The container object
40186      */
40187     container : function(c){
40188         var l = new Roo.form.Layout(c);
40189         this.start(l);
40190         if(arguments.length > 1){ // duplicate code required because of Opera
40191             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40192             this.end();
40193         }
40194         return l;
40195     },
40196
40197     /**
40198      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40199      * @param {Object} container A Roo.form.Layout or subclass of Layout
40200      * @return {Form} this
40201      */
40202     start : function(c){
40203         // cascade label info
40204         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40205         this.active.stack.push(c);
40206         c.ownerCt = this.active;
40207         this.active = c;
40208         return this;
40209     },
40210
40211     /**
40212      * Closes the current open container
40213      * @return {Form} this
40214      */
40215     end : function(){
40216         if(this.active == this.root){
40217             return this;
40218         }
40219         this.active = this.active.ownerCt;
40220         return this;
40221     },
40222
40223     /**
40224      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40225      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40226      * as the label of the field.
40227      * @param {Field} field1
40228      * @param {Field} field2 (optional)
40229      * @param {Field} etc. (optional)
40230      * @return {Form} this
40231      */
40232     add : function(){
40233         this.active.stack.push.apply(this.active.stack, arguments);
40234         this.allItems.push.apply(this.allItems,arguments);
40235         var r = [];
40236         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40237             if(a[i].isFormField){
40238                 r.push(a[i]);
40239             }
40240         }
40241         if(r.length > 0){
40242             Roo.form.Form.superclass.add.apply(this, r);
40243         }
40244         return this;
40245     },
40246      /**
40247      * Find any element that has been added to a form, using it's ID or name
40248      * This can include framesets, columns etc. along with regular fields..
40249      * @param {String} id - id or name to find.
40250      
40251      * @return {Element} e - or false if nothing found.
40252      */
40253     findbyId : function(id)
40254     {
40255         var ret = false;
40256         if (!id) {
40257             return ret;
40258         }
40259         Ext.each(this.allItems, function(f){
40260             if (f.id == id || f.name == id ){
40261                 ret = f;
40262                 return false;
40263             }
40264         });
40265         return ret;
40266     },
40267
40268     
40269     
40270     /**
40271      * Render this form into the passed container. This should only be called once!
40272      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40273      * @return {Form} this
40274      */
40275     render : function(ct){
40276         ct = Roo.get(ct);
40277         var o = this.autoCreate || {
40278             tag: 'form',
40279             method : this.method || 'POST',
40280             id : this.id || Roo.id()
40281         };
40282         this.initEl(ct.createChild(o));
40283
40284         this.root.render(this.el);
40285
40286         this.items.each(function(f){
40287             f.render('x-form-el-'+f.id);
40288         });
40289
40290         if(this.buttons.length > 0){
40291             // tables are required to maintain order and for correct IE layout
40292             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40293                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40294                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40295             }}, null, true);
40296             var tr = tb.getElementsByTagName('tr')[0];
40297             for(var i = 0, len = this.buttons.length; i < len; i++) {
40298                 var b = this.buttons[i];
40299                 var td = document.createElement('td');
40300                 td.className = 'x-form-btn-td';
40301                 b.render(tr.appendChild(td));
40302             }
40303         }
40304         if(this.monitorValid){ // initialize after render
40305             this.startMonitoring();
40306         }
40307         this.fireEvent('rendered', this);
40308         return this;
40309     },
40310
40311     /**
40312      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40313      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40314      * object or a valid Roo.DomHelper element config
40315      * @param {Function} handler The function called when the button is clicked
40316      * @param {Object} scope (optional) The scope of the handler function
40317      * @return {Roo.Button}
40318      */
40319     addButton : function(config, handler, scope){
40320         var bc = {
40321             handler: handler,
40322             scope: scope,
40323             minWidth: this.minButtonWidth,
40324             hideParent:true
40325         };
40326         if(typeof config == "string"){
40327             bc.text = config;
40328         }else{
40329             Roo.apply(bc, config);
40330         }
40331         var btn = new Roo.Button(null, bc);
40332         this.buttons.push(btn);
40333         return btn;
40334     },
40335
40336      /**
40337      * Adds a series of form elements (using the xtype property as the factory method.
40338      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40339      * @param {Object} config 
40340      */
40341     
40342     addxtype : function()
40343     {
40344         var ar = Array.prototype.slice.call(arguments, 0);
40345         var ret = false;
40346         for(var i = 0; i < ar.length; i++) {
40347             if (!ar[i]) {
40348                 continue; // skip -- if this happends something invalid got sent, we 
40349                 // should ignore it, as basically that interface element will not show up
40350                 // and that should be pretty obvious!!
40351             }
40352             
40353             if (Roo.form[ar[i].xtype]) {
40354                 ar[i].form = this;
40355                 var fe = Roo.factory(ar[i], Roo.form);
40356                 if (!ret) {
40357                     ret = fe;
40358                 }
40359                 fe.form = this;
40360                 if (fe.store) {
40361                     fe.store.form = this;
40362                 }
40363                 if (fe.isLayout) {  
40364                          
40365                     this.start(fe);
40366                     this.allItems.push(fe);
40367                     if (fe.items && fe.addxtype) {
40368                         fe.addxtype.apply(fe, fe.items);
40369                         delete fe.items;
40370                     }
40371                      this.end();
40372                     continue;
40373                 }
40374                 
40375                 
40376                  
40377                 this.add(fe);
40378               //  console.log('adding ' + ar[i].xtype);
40379             }
40380             if (ar[i].xtype == 'Button') {  
40381                 //console.log('adding button');
40382                 //console.log(ar[i]);
40383                 this.addButton(ar[i]);
40384                 this.allItems.push(fe);
40385                 continue;
40386             }
40387             
40388             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40389                 alert('end is not supported on xtype any more, use items');
40390             //    this.end();
40391             //    //console.log('adding end');
40392             }
40393             
40394         }
40395         return ret;
40396     },
40397     
40398     /**
40399      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40400      * option "monitorValid"
40401      */
40402     startMonitoring : function(){
40403         if(!this.bound){
40404             this.bound = true;
40405             Roo.TaskMgr.start({
40406                 run : this.bindHandler,
40407                 interval : this.monitorPoll || 200,
40408                 scope: this
40409             });
40410         }
40411     },
40412
40413     /**
40414      * Stops monitoring of the valid state of this form
40415      */
40416     stopMonitoring : function(){
40417         this.bound = false;
40418     },
40419
40420     // private
40421     bindHandler : function(){
40422         if(!this.bound){
40423             return false; // stops binding
40424         }
40425         var valid = true;
40426         this.items.each(function(f){
40427             if(!f.isValid(true)){
40428                 valid = false;
40429                 return false;
40430             }
40431         });
40432         for(var i = 0, len = this.buttons.length; i < len; i++){
40433             var btn = this.buttons[i];
40434             if(btn.formBind === true && btn.disabled === valid){
40435                 btn.setDisabled(!valid);
40436             }
40437         }
40438         this.fireEvent('clientvalidation', this, valid);
40439     }
40440     
40441     
40442     
40443     
40444     
40445     
40446     
40447     
40448 });
40449
40450
40451 // back compat
40452 Roo.Form = Roo.form.Form;
40453 /*
40454  * Based on:
40455  * Ext JS Library 1.1.1
40456  * Copyright(c) 2006-2007, Ext JS, LLC.
40457  *
40458  * Originally Released Under LGPL - original licence link has changed is not relivant.
40459  *
40460  * Fork - LGPL
40461  * <script type="text/javascript">
40462  */
40463  
40464  /**
40465  * @class Roo.form.Action
40466  * Internal Class used to handle form actions
40467  * @constructor
40468  * @param {Roo.form.BasicForm} el The form element or its id
40469  * @param {Object} config Configuration options
40470  */
40471  
40472  
40473 // define the action interface
40474 Roo.form.Action = function(form, options){
40475     this.form = form;
40476     this.options = options || {};
40477 };
40478 /**
40479  * Client Validation Failed
40480  * @const 
40481  */
40482 Roo.form.Action.CLIENT_INVALID = 'client';
40483 /**
40484  * Server Validation Failed
40485  * @const 
40486  */
40487  Roo.form.Action.SERVER_INVALID = 'server';
40488  /**
40489  * Connect to Server Failed
40490  * @const 
40491  */
40492 Roo.form.Action.CONNECT_FAILURE = 'connect';
40493 /**
40494  * Reading Data from Server Failed
40495  * @const 
40496  */
40497 Roo.form.Action.LOAD_FAILURE = 'load';
40498
40499 Roo.form.Action.prototype = {
40500     type : 'default',
40501     failureType : undefined,
40502     response : undefined,
40503     result : undefined,
40504
40505     // interface method
40506     run : function(options){
40507
40508     },
40509
40510     // interface method
40511     success : function(response){
40512
40513     },
40514
40515     // interface method
40516     handleResponse : function(response){
40517
40518     },
40519
40520     // default connection failure
40521     failure : function(response){
40522         this.response = response;
40523         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40524         this.form.afterAction(this, false);
40525     },
40526
40527     processResponse : function(response){
40528         this.response = response;
40529         if(!response.responseText){
40530             return true;
40531         }
40532         this.result = this.handleResponse(response);
40533         return this.result;
40534     },
40535
40536     // utility functions used internally
40537     getUrl : function(appendParams){
40538         var url = this.options.url || this.form.url || this.form.el.dom.action;
40539         if(appendParams){
40540             var p = this.getParams();
40541             if(p){
40542                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40543             }
40544         }
40545         return url;
40546     },
40547
40548     getMethod : function(){
40549         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40550     },
40551
40552     getParams : function(){
40553         var bp = this.form.baseParams;
40554         var p = this.options.params;
40555         if(p){
40556             if(typeof p == "object"){
40557                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40558             }else if(typeof p == 'string' && bp){
40559                 p += '&' + Roo.urlEncode(bp);
40560             }
40561         }else if(bp){
40562             p = Roo.urlEncode(bp);
40563         }
40564         return p;
40565     },
40566
40567     createCallback : function(){
40568         return {
40569             success: this.success,
40570             failure: this.failure,
40571             scope: this,
40572             timeout: (this.form.timeout*1000),
40573             upload: this.form.fileUpload ? this.success : undefined
40574         };
40575     }
40576 };
40577
40578 Roo.form.Action.Submit = function(form, options){
40579     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40580 };
40581
40582 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40583     type : 'submit',
40584
40585     run : function(){
40586         var o = this.options;
40587         var method = this.getMethod();
40588         var isPost = method == 'POST';
40589         if(o.clientValidation === false || this.form.isValid()){
40590             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40591                 form:this.form.el.dom,
40592                 url:this.getUrl(!isPost),
40593                 method: method,
40594                 params:isPost ? this.getParams() : null,
40595                 isUpload: this.form.fileUpload
40596             }));
40597
40598         }else if (o.clientValidation !== false){ // client validation failed
40599             this.failureType = Roo.form.Action.CLIENT_INVALID;
40600             this.form.afterAction(this, false);
40601         }
40602     },
40603
40604     success : function(response){
40605         var result = this.processResponse(response);
40606         if(result === true || result.success){
40607             this.form.afterAction(this, true);
40608             return;
40609         }
40610         if(result.errors){
40611             this.form.markInvalid(result.errors);
40612             this.failureType = Roo.form.Action.SERVER_INVALID;
40613         }
40614         this.form.afterAction(this, false);
40615     },
40616
40617     handleResponse : function(response){
40618         if(this.form.errorReader){
40619             var rs = this.form.errorReader.read(response);
40620             var errors = [];
40621             if(rs.records){
40622                 for(var i = 0, len = rs.records.length; i < len; i++) {
40623                     var r = rs.records[i];
40624                     errors[i] = r.data;
40625                 }
40626             }
40627             if(errors.length < 1){
40628                 errors = null;
40629             }
40630             return {
40631                 success : rs.success,
40632                 errors : errors
40633             };
40634         }
40635         var ret = false;
40636         try {
40637             ret = Roo.decode(response.responseText);
40638         } catch (e) {
40639             ret = {
40640                 success: false,
40641                 errorMsg: "Failed to read server message: " + response.responseText,
40642                 errors : []
40643             };
40644         }
40645         return ret;
40646         
40647     }
40648 });
40649
40650
40651 Roo.form.Action.Load = function(form, options){
40652     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40653     this.reader = this.form.reader;
40654 };
40655
40656 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40657     type : 'load',
40658
40659     run : function(){
40660         Roo.Ajax.request(Roo.apply(
40661                 this.createCallback(), {
40662                     method:this.getMethod(),
40663                     url:this.getUrl(false),
40664                     params:this.getParams()
40665         }));
40666     },
40667
40668     success : function(response){
40669         var result = this.processResponse(response);
40670         if(result === true || !result.success || !result.data){
40671             this.failureType = Roo.form.Action.LOAD_FAILURE;
40672             this.form.afterAction(this, false);
40673             return;
40674         }
40675         this.form.clearInvalid();
40676         this.form.setValues(result.data);
40677         this.form.afterAction(this, true);
40678     },
40679
40680     handleResponse : function(response){
40681         if(this.form.reader){
40682             var rs = this.form.reader.read(response);
40683             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40684             return {
40685                 success : rs.success,
40686                 data : data
40687             };
40688         }
40689         return Roo.decode(response.responseText);
40690     }
40691 });
40692
40693 Roo.form.Action.ACTION_TYPES = {
40694     'load' : Roo.form.Action.Load,
40695     'submit' : Roo.form.Action.Submit
40696 };/*
40697  * Based on:
40698  * Ext JS Library 1.1.1
40699  * Copyright(c) 2006-2007, Ext JS, LLC.
40700  *
40701  * Originally Released Under LGPL - original licence link has changed is not relivant.
40702  *
40703  * Fork - LGPL
40704  * <script type="text/javascript">
40705  */
40706  
40707 /**
40708  * @class Roo.form.Layout
40709  * @extends Roo.Component
40710  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40711  * @constructor
40712  * @param {Object} config Configuration options
40713  */
40714 Roo.form.Layout = function(config){
40715     var xitems = [];
40716     if (config.items) {
40717         xitems = config.items;
40718         delete config.items;
40719     }
40720     Roo.form.Layout.superclass.constructor.call(this, config);
40721     this.stack = [];
40722     Roo.each(xitems, this.addxtype, this);
40723      
40724 };
40725
40726 Roo.extend(Roo.form.Layout, Roo.Component, {
40727     /**
40728      * @cfg {String/Object} autoCreate
40729      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40730      */
40731     /**
40732      * @cfg {String/Object/Function} style
40733      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40734      * a function which returns such a specification.
40735      */
40736     /**
40737      * @cfg {String} labelAlign
40738      * Valid values are "left," "top" and "right" (defaults to "left")
40739      */
40740     /**
40741      * @cfg {Number} labelWidth
40742      * Fixed width in pixels of all field labels (defaults to undefined)
40743      */
40744     /**
40745      * @cfg {Boolean} clear
40746      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40747      */
40748     clear : true,
40749     /**
40750      * @cfg {String} labelSeparator
40751      * The separator to use after field labels (defaults to ':')
40752      */
40753     labelSeparator : ':',
40754     /**
40755      * @cfg {Boolean} hideLabels
40756      * True to suppress the display of field labels in this layout (defaults to false)
40757      */
40758     hideLabels : false,
40759
40760     // private
40761     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40762     
40763     isLayout : true,
40764     
40765     // private
40766     onRender : function(ct, position){
40767         if(this.el){ // from markup
40768             this.el = Roo.get(this.el);
40769         }else {  // generate
40770             var cfg = this.getAutoCreate();
40771             this.el = ct.createChild(cfg, position);
40772         }
40773         if(this.style){
40774             this.el.applyStyles(this.style);
40775         }
40776         if(this.labelAlign){
40777             this.el.addClass('x-form-label-'+this.labelAlign);
40778         }
40779         if(this.hideLabels){
40780             this.labelStyle = "display:none";
40781             this.elementStyle = "padding-left:0;";
40782         }else{
40783             if(typeof this.labelWidth == 'number'){
40784                 this.labelStyle = "width:"+this.labelWidth+"px;";
40785                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
40786             }
40787             if(this.labelAlign == 'top'){
40788                 this.labelStyle = "width:auto;";
40789                 this.elementStyle = "padding-left:0;";
40790             }
40791         }
40792         var stack = this.stack;
40793         var slen = stack.length;
40794         if(slen > 0){
40795             if(!this.fieldTpl){
40796                 var t = new Roo.Template(
40797                     '<div class="x-form-item {5}">',
40798                         '<label for="{0}" style="{2}">{1}{4}</label>',
40799                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40800                         '</div>',
40801                     '</div><div class="x-form-clear-left"></div>'
40802                 );
40803                 t.disableFormats = true;
40804                 t.compile();
40805                 Roo.form.Layout.prototype.fieldTpl = t;
40806             }
40807             for(var i = 0; i < slen; i++) {
40808                 if(stack[i].isFormField){
40809                     this.renderField(stack[i]);
40810                 }else{
40811                     this.renderComponent(stack[i]);
40812                 }
40813             }
40814         }
40815         if(this.clear){
40816             this.el.createChild({cls:'x-form-clear'});
40817         }
40818     },
40819
40820     // private
40821     renderField : function(f){
40822         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
40823                f.id, //0
40824                f.fieldLabel, //1
40825                f.labelStyle||this.labelStyle||'', //2
40826                this.elementStyle||'', //3
40827                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
40828                f.itemCls||this.itemCls||''  //5
40829        ], true).getPrevSibling());
40830     },
40831
40832     // private
40833     renderComponent : function(c){
40834         c.render(c.isLayout ? this.el : this.el.createChild());    
40835     },
40836     /**
40837      * Adds a object form elements (using the xtype property as the factory method.)
40838      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
40839      * @param {Object} config 
40840      */
40841     addxtype : function(o)
40842     {
40843         // create the lement.
40844         o.form = this.form;
40845         var fe = Roo.factory(o, Roo.form);
40846         this.form.allItems.push(fe);
40847         this.stack.push(fe);
40848         
40849         if (fe.isFormField) {
40850             this.form.items.add(fe);
40851         }
40852          
40853         return fe;
40854     }
40855 });
40856
40857 /**
40858  * @class Roo.form.Column
40859  * @extends Roo.form.Layout
40860  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
40861  * @constructor
40862  * @param {Object} config Configuration options
40863  */
40864 Roo.form.Column = function(config){
40865     Roo.form.Column.superclass.constructor.call(this, config);
40866 };
40867
40868 Roo.extend(Roo.form.Column, Roo.form.Layout, {
40869     /**
40870      * @cfg {Number/String} width
40871      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40872      */
40873     /**
40874      * @cfg {String/Object} autoCreate
40875      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
40876      */
40877
40878     // private
40879     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
40880
40881     // private
40882     onRender : function(ct, position){
40883         Roo.form.Column.superclass.onRender.call(this, ct, position);
40884         if(this.width){
40885             this.el.setWidth(this.width);
40886         }
40887     }
40888 });
40889
40890
40891 /**
40892  * @class Roo.form.Row
40893  * @extends Roo.form.Layout
40894  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
40895  * @constructor
40896  * @param {Object} config Configuration options
40897  */
40898
40899  
40900 Roo.form.Row = function(config){
40901     Roo.form.Row.superclass.constructor.call(this, config);
40902 };
40903  
40904 Roo.extend(Roo.form.Row, Roo.form.Layout, {
40905       /**
40906      * @cfg {Number/String} width
40907      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40908      */
40909     /**
40910      * @cfg {Number/String} height
40911      * The fixed height of the column in pixels or CSS value (defaults to "auto")
40912      */
40913     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
40914     
40915     padWidth : 20,
40916     // private
40917     onRender : function(ct, position){
40918         //console.log('row render');
40919         if(!this.rowTpl){
40920             var t = new Roo.Template(
40921                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
40922                     '<label for="{0}" style="{2}">{1}{4}</label>',
40923                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40924                     '</div>',
40925                 '</div>'
40926             );
40927             t.disableFormats = true;
40928             t.compile();
40929             Roo.form.Layout.prototype.rowTpl = t;
40930         }
40931         this.fieldTpl = this.rowTpl;
40932         
40933         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
40934         var labelWidth = 100;
40935         
40936         if ((this.labelAlign != 'top')) {
40937             if (typeof this.labelWidth == 'number') {
40938                 labelWidth = this.labelWidth
40939             }
40940             this.padWidth =  20 + labelWidth;
40941             
40942         }
40943         
40944         Roo.form.Column.superclass.onRender.call(this, ct, position);
40945         if(this.width){
40946             this.el.setWidth(this.width);
40947         }
40948         if(this.height){
40949             this.el.setHeight(this.height);
40950         }
40951     },
40952     
40953     // private
40954     renderField : function(f){
40955         f.fieldEl = this.fieldTpl.append(this.el, [
40956                f.id, f.fieldLabel,
40957                f.labelStyle||this.labelStyle||'',
40958                this.elementStyle||'',
40959                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
40960                f.itemCls||this.itemCls||'',
40961                f.width ? f.width + this.padWidth : 160 + this.padWidth
40962        ],true);
40963     }
40964 });
40965  
40966
40967 /**
40968  * @class Roo.form.FieldSet
40969  * @extends Roo.form.Layout
40970  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
40971  * @constructor
40972  * @param {Object} config Configuration options
40973  */
40974 Roo.form.FieldSet = function(config){
40975     Roo.form.FieldSet.superclass.constructor.call(this, config);
40976 };
40977
40978 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
40979     /**
40980      * @cfg {String} legend
40981      * The text to display as the legend for the FieldSet (defaults to '')
40982      */
40983     /**
40984      * @cfg {String/Object} autoCreate
40985      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
40986      */
40987
40988     // private
40989     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
40990
40991     // private
40992     onRender : function(ct, position){
40993         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
40994         if(this.legend){
40995             this.setLegend(this.legend);
40996         }
40997     },
40998
40999     // private
41000     setLegend : function(text){
41001         if(this.rendered){
41002             this.el.child('legend').update(text);
41003         }
41004     }
41005 });/*
41006  * Based on:
41007  * Ext JS Library 1.1.1
41008  * Copyright(c) 2006-2007, Ext JS, LLC.
41009  *
41010  * Originally Released Under LGPL - original licence link has changed is not relivant.
41011  *
41012  * Fork - LGPL
41013  * <script type="text/javascript">
41014  */
41015 /**
41016  * @class Roo.form.VTypes
41017  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41018  * @singleton
41019  */
41020 Roo.form.VTypes = function(){
41021     // closure these in so they are only created once.
41022     var alpha = /^[a-zA-Z_]+$/;
41023     var alphanum = /^[a-zA-Z0-9_]+$/;
41024     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41025     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41026
41027     // All these messages and functions are configurable
41028     return {
41029         /**
41030          * The function used to validate email addresses
41031          * @param {String} value The email address
41032          */
41033         'email' : function(v){
41034             return email.test(v);
41035         },
41036         /**
41037          * The error text to display when the email validation function returns false
41038          * @type String
41039          */
41040         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41041         /**
41042          * The keystroke filter mask to be applied on email input
41043          * @type RegExp
41044          */
41045         'emailMask' : /[a-z0-9_\.\-@]/i,
41046
41047         /**
41048          * The function used to validate URLs
41049          * @param {String} value The URL
41050          */
41051         'url' : function(v){
41052             return url.test(v);
41053         },
41054         /**
41055          * The error text to display when the url validation function returns false
41056          * @type String
41057          */
41058         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41059         
41060         /**
41061          * The function used to validate alpha values
41062          * @param {String} value The value
41063          */
41064         'alpha' : function(v){
41065             return alpha.test(v);
41066         },
41067         /**
41068          * The error text to display when the alpha validation function returns false
41069          * @type String
41070          */
41071         'alphaText' : 'This field should only contain letters and _',
41072         /**
41073          * The keystroke filter mask to be applied on alpha input
41074          * @type RegExp
41075          */
41076         'alphaMask' : /[a-z_]/i,
41077
41078         /**
41079          * The function used to validate alphanumeric values
41080          * @param {String} value The value
41081          */
41082         'alphanum' : function(v){
41083             return alphanum.test(v);
41084         },
41085         /**
41086          * The error text to display when the alphanumeric validation function returns false
41087          * @type String
41088          */
41089         'alphanumText' : 'This field should only contain letters, numbers and _',
41090         /**
41091          * The keystroke filter mask to be applied on alphanumeric input
41092          * @type RegExp
41093          */
41094         'alphanumMask' : /[a-z0-9_]/i
41095     };
41096 }();//<script type="text/javascript">
41097
41098 /**
41099  * @class Roo.form.FCKeditor
41100  * @extends Roo.form.TextArea
41101  * Wrapper around the FCKEditor http://www.fckeditor.net
41102  * @constructor
41103  * Creates a new FCKeditor
41104  * @param {Object} config Configuration options
41105  */
41106 Roo.form.FCKeditor = function(config){
41107     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41108     this.addEvents({
41109          /**
41110          * @event editorinit
41111          * Fired when the editor is initialized - you can add extra handlers here..
41112          * @param {FCKeditor} this
41113          * @param {Object} the FCK object.
41114          */
41115         editorinit : true
41116     });
41117     
41118     
41119 };
41120 Roo.form.FCKeditor.editors = { };
41121 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41122 {
41123     //defaultAutoCreate : {
41124     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41125     //},
41126     // private
41127     /**
41128      * @cfg {Object} fck options - see fck manual for details.
41129      */
41130     fckconfig : false,
41131     
41132     /**
41133      * @cfg {Object} fck toolbar set (Basic or Default)
41134      */
41135     toolbarSet : 'Basic',
41136     /**
41137      * @cfg {Object} fck BasePath
41138      */ 
41139     basePath : '/fckeditor/',
41140     
41141     
41142     frame : false,
41143     
41144     value : '',
41145     
41146    
41147     onRender : function(ct, position)
41148     {
41149         if(!this.el){
41150             this.defaultAutoCreate = {
41151                 tag: "textarea",
41152                 style:"width:300px;height:60px;",
41153                 autocomplete: "off"
41154             };
41155         }
41156         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41157         /*
41158         if(this.grow){
41159             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41160             if(this.preventScrollbars){
41161                 this.el.setStyle("overflow", "hidden");
41162             }
41163             this.el.setHeight(this.growMin);
41164         }
41165         */
41166         //console.log('onrender' + this.getId() );
41167         Roo.form.FCKeditor.editors[this.getId()] = this;
41168          
41169
41170         this.replaceTextarea() ;
41171         
41172     },
41173     
41174     getEditor : function() {
41175         return this.fckEditor;
41176     },
41177     /**
41178      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41179      * @param {Mixed} value The value to set
41180      */
41181     
41182     
41183     setValue : function(value)
41184     {
41185         //console.log('setValue: ' + value);
41186         
41187         if(typeof(value) == 'undefined') { // not sure why this is happending...
41188             return;
41189         }
41190         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41191         
41192         //if(!this.el || !this.getEditor()) {
41193         //    this.value = value;
41194             //this.setValue.defer(100,this,[value]);    
41195         //    return;
41196         //} 
41197         
41198         if(!this.getEditor()) {
41199             return;
41200         }
41201         
41202         this.getEditor().SetData(value);
41203         
41204         //
41205
41206     },
41207
41208     /**
41209      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41210      * @return {Mixed} value The field value
41211      */
41212     getValue : function()
41213     {
41214         
41215         if (this.frame && this.frame.dom.style.display == 'none') {
41216             return Roo.form.FCKeditor.superclass.getValue.call(this);
41217         }
41218         
41219         if(!this.el || !this.getEditor()) {
41220            
41221            // this.getValue.defer(100,this); 
41222             return this.value;
41223         }
41224        
41225         
41226         var value=this.getEditor().GetData();
41227         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41228         return Roo.form.FCKeditor.superclass.getValue.call(this);
41229         
41230
41231     },
41232
41233     /**
41234      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41235      * @return {Mixed} value The field value
41236      */
41237     getRawValue : function()
41238     {
41239         if (this.frame && this.frame.dom.style.display == 'none') {
41240             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41241         }
41242         
41243         if(!this.el || !this.getEditor()) {
41244             //this.getRawValue.defer(100,this); 
41245             return this.value;
41246             return;
41247         }
41248         
41249         
41250         
41251         var value=this.getEditor().GetData();
41252         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41253         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41254          
41255     },
41256     
41257     setSize : function(w,h) {
41258         
41259         
41260         
41261         //if (this.frame && this.frame.dom.style.display == 'none') {
41262         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41263         //    return;
41264         //}
41265         //if(!this.el || !this.getEditor()) {
41266         //    this.setSize.defer(100,this, [w,h]); 
41267         //    return;
41268         //}
41269         
41270         
41271         
41272         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41273         
41274         this.frame.dom.setAttribute('width', w);
41275         this.frame.dom.setAttribute('height', h);
41276         this.frame.setSize(w,h);
41277         
41278     },
41279     
41280     toggleSourceEdit : function(value) {
41281         
41282       
41283          
41284         this.el.dom.style.display = value ? '' : 'none';
41285         this.frame.dom.style.display = value ?  'none' : '';
41286         
41287     },
41288     
41289     
41290     focus: function(tag)
41291     {
41292         if (this.frame.dom.style.display == 'none') {
41293             return Roo.form.FCKeditor.superclass.focus.call(this);
41294         }
41295         if(!this.el || !this.getEditor()) {
41296             this.focus.defer(100,this, [tag]); 
41297             return;
41298         }
41299         
41300         
41301         
41302         
41303         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41304         this.getEditor().Focus();
41305         if (tgs.length) {
41306             if (!this.getEditor().Selection.GetSelection()) {
41307                 this.focus.defer(100,this, [tag]); 
41308                 return;
41309             }
41310             
41311             
41312             var r = this.getEditor().EditorDocument.createRange();
41313             r.setStart(tgs[0],0);
41314             r.setEnd(tgs[0],0);
41315             this.getEditor().Selection.GetSelection().removeAllRanges();
41316             this.getEditor().Selection.GetSelection().addRange(r);
41317             this.getEditor().Focus();
41318         }
41319         
41320     },
41321     
41322     
41323     
41324     replaceTextarea : function()
41325     {
41326         if ( document.getElementById( this.getId() + '___Frame' ) )
41327             return ;
41328         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41329         //{
41330             // We must check the elements firstly using the Id and then the name.
41331         var oTextarea = document.getElementById( this.getId() );
41332         
41333         var colElementsByName = document.getElementsByName( this.getId() ) ;
41334          
41335         oTextarea.style.display = 'none' ;
41336
41337         if ( oTextarea.tabIndex ) {            
41338             this.TabIndex = oTextarea.tabIndex ;
41339         }
41340         
41341         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41342         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41343         this.frame = Roo.get(this.getId() + '___Frame')
41344     },
41345     
41346     _getConfigHtml : function()
41347     {
41348         var sConfig = '' ;
41349
41350         for ( var o in this.fckconfig ) {
41351             sConfig += sConfig.length > 0  ? '&amp;' : '';
41352             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41353         }
41354
41355         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41356     },
41357     
41358     
41359     _getIFrameHtml : function()
41360     {
41361         var sFile = 'fckeditor.html' ;
41362         /* no idea what this is about..
41363         try
41364         {
41365             if ( (/fcksource=true/i).test( window.top.location.search ) )
41366                 sFile = 'fckeditor.original.html' ;
41367         }
41368         catch (e) { 
41369         */
41370
41371         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41372         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41373         
41374         
41375         var html = '<iframe id="' + this.getId() +
41376             '___Frame" src="' + sLink +
41377             '" width="' + this.width +
41378             '" height="' + this.height + '"' +
41379             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41380             ' frameborder="0" scrolling="no"></iframe>' ;
41381
41382         return html ;
41383     },
41384     
41385     _insertHtmlBefore : function( html, element )
41386     {
41387         if ( element.insertAdjacentHTML )       {
41388             // IE
41389             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41390         } else { // Gecko
41391             var oRange = document.createRange() ;
41392             oRange.setStartBefore( element ) ;
41393             var oFragment = oRange.createContextualFragment( html );
41394             element.parentNode.insertBefore( oFragment, element ) ;
41395         }
41396     }
41397     
41398     
41399   
41400     
41401     
41402     
41403     
41404
41405 });
41406
41407 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41408
41409 function FCKeditor_OnComplete(editorInstance){
41410     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41411     f.fckEditor = editorInstance;
41412     //console.log("loaded");
41413     f.fireEvent('editorinit', f, editorInstance);
41414
41415   
41416
41417  
41418
41419
41420
41421
41422
41423
41424
41425
41426
41427
41428
41429
41430
41431
41432
41433 //<script type="text/javascript">
41434 /**
41435  * @class Roo.form.GridField
41436  * @extends Roo.form.Field
41437  * Embed a grid (or editable grid into a form)
41438  * STATUS ALPHA
41439  * @constructor
41440  * Creates a new GridField
41441  * @param {Object} config Configuration options
41442  */
41443 Roo.form.GridField = function(config){
41444     Roo.form.GridField.superclass.constructor.call(this, config);
41445      
41446 };
41447
41448 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41449     /**
41450      * @cfg {Number} width  - used to restrict width of grid..
41451      */
41452     width : 100,
41453     /**
41454      * @cfg {Number} height - used to restrict height of grid..
41455      */
41456     height : 50,
41457      /**
41458      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41459      */
41460     xgrid : false, 
41461     /**
41462      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41463      * {tag: "input", type: "checkbox", autocomplete: "off"})
41464      */
41465    // defaultAutoCreate : { tag: 'div' },
41466     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41467     /**
41468      * @cfg {String} addTitle Text to include for adding a title.
41469      */
41470     addTitle : false,
41471     //
41472     onResize : function(){
41473         Roo.form.Field.superclass.onResize.apply(this, arguments);
41474     },
41475
41476     initEvents : function(){
41477         // Roo.form.Checkbox.superclass.initEvents.call(this);
41478         // has no events...
41479        
41480     },
41481
41482
41483     getResizeEl : function(){
41484         return this.wrap;
41485     },
41486
41487     getPositionEl : function(){
41488         return this.wrap;
41489     },
41490
41491     // private
41492     onRender : function(ct, position){
41493         
41494         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41495         var style = this.style;
41496         delete this.style;
41497         
41498         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41499         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41500         this.viewEl = this.wrap.createChild({ tag: 'div' });
41501         if (style) {
41502             this.viewEl.applyStyles(style);
41503         }
41504         if (this.width) {
41505             this.viewEl.setWidth(this.width);
41506         }
41507         if (this.height) {
41508             this.viewEl.setHeight(this.height);
41509         }
41510         //if(this.inputValue !== undefined){
41511         //this.setValue(this.value);
41512         
41513         
41514         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41515         
41516         
41517         this.grid.render();
41518         this.grid.getDataSource().on('remove', this.refreshValue, this);
41519         this.grid.getDataSource().on('update', this.refreshValue, this);
41520         this.grid.on('afteredit', this.refreshValue, this);
41521  
41522     },
41523      
41524     
41525     /**
41526      * Sets the value of the item. 
41527      * @param {String} either an object  or a string..
41528      */
41529     setValue : function(v){
41530         //this.value = v;
41531         v = v || []; // empty set..
41532         // this does not seem smart - it really only affects memoryproxy grids..
41533         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41534             var ds = this.grid.getDataSource();
41535             // assumes a json reader..
41536             var data = {}
41537             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41538             ds.loadData( data);
41539         }
41540         Roo.form.GridField.superclass.setValue.call(this, v);
41541         this.refreshValue();
41542         // should load data in the grid really....
41543     },
41544     
41545     // private
41546     refreshValue: function() {
41547          var val = [];
41548         this.grid.getDataSource().each(function(r) {
41549             val.push(r.data);
41550         });
41551         this.el.dom.value = Roo.encode(val);
41552     }
41553     
41554      
41555     
41556     
41557 });//<script type="text/javasscript">
41558  
41559
41560 /**
41561  * @class Roo.DDView
41562  * A DnD enabled version of Roo.View.
41563  * @param {Element/String} container The Element in which to create the View.
41564  * @param {String} tpl The template string used to create the markup for each element of the View
41565  * @param {Object} config The configuration properties. These include all the config options of
41566  * {@link Roo.View} plus some specific to this class.<br>
41567  * <p>
41568  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41569  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41570  * <p>
41571  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41572 .x-view-drag-insert-above {
41573         border-top:1px dotted #3366cc;
41574 }
41575 .x-view-drag-insert-below {
41576         border-bottom:1px dotted #3366cc;
41577 }
41578 </code></pre>
41579  * 
41580  */
41581  
41582 Roo.DDView = function(container, tpl, config) {
41583     Roo.DDView.superclass.constructor.apply(this, arguments);
41584     this.getEl().setStyle("outline", "0px none");
41585     this.getEl().unselectable();
41586     if (this.dragGroup) {
41587                 this.setDraggable(this.dragGroup.split(","));
41588     }
41589     if (this.dropGroup) {
41590                 this.setDroppable(this.dropGroup.split(","));
41591     }
41592     if (this.deletable) {
41593         this.setDeletable();
41594     }
41595     this.isDirtyFlag = false;
41596         this.addEvents({
41597                 "drop" : true
41598         });
41599 };
41600
41601 Roo.extend(Roo.DDView, Roo.View, {
41602 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41603 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41604 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41605 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41606
41607         isFormField: true,
41608
41609         reset: Roo.emptyFn,
41610         
41611         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41612
41613         validate: function() {
41614                 return true;
41615         },
41616         
41617         destroy: function() {
41618                 this.purgeListeners();
41619                 this.getEl.removeAllListeners();
41620                 this.getEl().remove();
41621                 if (this.dragZone) {
41622                         if (this.dragZone.destroy) {
41623                                 this.dragZone.destroy();
41624                         }
41625                 }
41626                 if (this.dropZone) {
41627                         if (this.dropZone.destroy) {
41628                                 this.dropZone.destroy();
41629                         }
41630                 }
41631         },
41632
41633 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41634         getName: function() {
41635                 return this.name;
41636         },
41637
41638 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41639         setValue: function(v) {
41640                 if (!this.store) {
41641                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41642                 }
41643                 var data = {};
41644                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41645                 this.store.proxy = new Roo.data.MemoryProxy(data);
41646                 this.store.load();
41647         },
41648
41649 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41650         getValue: function() {
41651                 var result = '(';
41652                 this.store.each(function(rec) {
41653                         result += rec.id + ',';
41654                 });
41655                 return result.substr(0, result.length - 1) + ')';
41656         },
41657         
41658         getIds: function() {
41659                 var i = 0, result = new Array(this.store.getCount());
41660                 this.store.each(function(rec) {
41661                         result[i++] = rec.id;
41662                 });
41663                 return result;
41664         },
41665         
41666         isDirty: function() {
41667                 return this.isDirtyFlag;
41668         },
41669
41670 /**
41671  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41672  *      whole Element becomes the target, and this causes the drop gesture to append.
41673  */
41674     getTargetFromEvent : function(e) {
41675                 var target = e.getTarget();
41676                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41677                 target = target.parentNode;
41678                 }
41679                 if (!target) {
41680                         target = this.el.dom.lastChild || this.el.dom;
41681                 }
41682                 return target;
41683     },
41684
41685 /**
41686  *      Create the drag data which consists of an object which has the property "ddel" as
41687  *      the drag proxy element. 
41688  */
41689     getDragData : function(e) {
41690         var target = this.findItemFromChild(e.getTarget());
41691                 if(target) {
41692                         this.handleSelection(e);
41693                         var selNodes = this.getSelectedNodes();
41694             var dragData = {
41695                 source: this,
41696                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41697                 nodes: selNodes,
41698                 records: []
41699                         };
41700                         var selectedIndices = this.getSelectedIndexes();
41701                         for (var i = 0; i < selectedIndices.length; i++) {
41702                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41703                         }
41704                         if (selNodes.length == 1) {
41705                                 dragData.ddel = target.cloneNode(true); // the div element
41706                         } else {
41707                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41708                                 div.className = 'multi-proxy';
41709                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41710                                         div.appendChild(selNodes[i].cloneNode(true));
41711                                 }
41712                                 dragData.ddel = div;
41713                         }
41714             //console.log(dragData)
41715             //console.log(dragData.ddel.innerHTML)
41716                         return dragData;
41717                 }
41718         //console.log('nodragData')
41719                 return false;
41720     },
41721     
41722 /**     Specify to which ddGroup items in this DDView may be dragged. */
41723     setDraggable: function(ddGroup) {
41724         if (ddGroup instanceof Array) {
41725                 Roo.each(ddGroup, this.setDraggable, this);
41726                 return;
41727         }
41728         if (this.dragZone) {
41729                 this.dragZone.addToGroup(ddGroup);
41730         } else {
41731                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41732                                 containerScroll: true,
41733                                 ddGroup: ddGroup 
41734
41735                         });
41736 //                      Draggability implies selection. DragZone's mousedown selects the element.
41737                         if (!this.multiSelect) { this.singleSelect = true; }
41738
41739 //                      Wire the DragZone's handlers up to methods in *this*
41740                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41741                 }
41742     },
41743
41744 /**     Specify from which ddGroup this DDView accepts drops. */
41745     setDroppable: function(ddGroup) {
41746         if (ddGroup instanceof Array) {
41747                 Roo.each(ddGroup, this.setDroppable, this);
41748                 return;
41749         }
41750         if (this.dropZone) {
41751                 this.dropZone.addToGroup(ddGroup);
41752         } else {
41753                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41754                                 containerScroll: true,
41755                                 ddGroup: ddGroup
41756                         });
41757
41758 //                      Wire the DropZone's handlers up to methods in *this*
41759                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41760                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41761                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41762                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41763                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41764                 }
41765     },
41766
41767 /**     Decide whether to drop above or below a View node. */
41768     getDropPoint : function(e, n, dd){
41769         if (n == this.el.dom) { return "above"; }
41770                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
41771                 var c = t + (b - t) / 2;
41772                 var y = Roo.lib.Event.getPageY(e);
41773                 if(y <= c) {
41774                         return "above";
41775                 }else{
41776                         return "below";
41777                 }
41778     },
41779
41780     onNodeEnter : function(n, dd, e, data){
41781                 return false;
41782     },
41783     
41784     onNodeOver : function(n, dd, e, data){
41785                 var pt = this.getDropPoint(e, n, dd);
41786                 // set the insert point style on the target node
41787                 var dragElClass = this.dropNotAllowed;
41788                 if (pt) {
41789                         var targetElClass;
41790                         if (pt == "above"){
41791                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
41792                                 targetElClass = "x-view-drag-insert-above";
41793                         } else {
41794                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
41795                                 targetElClass = "x-view-drag-insert-below";
41796                         }
41797                         if (this.lastInsertClass != targetElClass){
41798                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
41799                                 this.lastInsertClass = targetElClass;
41800                         }
41801                 }
41802                 return dragElClass;
41803         },
41804
41805     onNodeOut : function(n, dd, e, data){
41806                 this.removeDropIndicators(n);
41807     },
41808
41809     onNodeDrop : function(n, dd, e, data){
41810         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
41811                 return false;
41812         }
41813         var pt = this.getDropPoint(e, n, dd);
41814                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
41815                 if (pt == "below") { insertAt++; }
41816                 for (var i = 0; i < data.records.length; i++) {
41817                         var r = data.records[i];
41818                         var dup = this.store.getById(r.id);
41819                         if (dup && (dd != this.dragZone)) {
41820                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
41821                         } else {
41822                                 if (data.copy) {
41823                                         this.store.insert(insertAt++, r.copy());
41824                                 } else {
41825                                         data.source.isDirtyFlag = true;
41826                                         r.store.remove(r);
41827                                         this.store.insert(insertAt++, r);
41828                                 }
41829                                 this.isDirtyFlag = true;
41830                         }
41831                 }
41832                 this.dragZone.cachedTarget = null;
41833                 return true;
41834     },
41835
41836     removeDropIndicators : function(n){
41837                 if(n){
41838                         Roo.fly(n).removeClass([
41839                                 "x-view-drag-insert-above",
41840                                 "x-view-drag-insert-below"]);
41841                         this.lastInsertClass = "_noclass";
41842                 }
41843     },
41844
41845 /**
41846  *      Utility method. Add a delete option to the DDView's context menu.
41847  *      @param {String} imageUrl The URL of the "delete" icon image.
41848  */
41849         setDeletable: function(imageUrl) {
41850                 if (!this.singleSelect && !this.multiSelect) {
41851                         this.singleSelect = true;
41852                 }
41853                 var c = this.getContextMenu();
41854                 this.contextMenu.on("itemclick", function(item) {
41855                         switch (item.id) {
41856                                 case "delete":
41857                                         this.remove(this.getSelectedIndexes());
41858                                         break;
41859                         }
41860                 }, this);
41861                 this.contextMenu.add({
41862                         icon: imageUrl,
41863                         id: "delete",
41864                         text: 'Delete'
41865                 });
41866         },
41867         
41868 /**     Return the context menu for this DDView. */
41869         getContextMenu: function() {
41870                 if (!this.contextMenu) {
41871 //                      Create the View's context menu
41872                         this.contextMenu = new Roo.menu.Menu({
41873                                 id: this.id + "-contextmenu"
41874                         });
41875                         this.el.on("contextmenu", this.showContextMenu, this);
41876                 }
41877                 return this.contextMenu;
41878         },
41879         
41880         disableContextMenu: function() {
41881                 if (this.contextMenu) {
41882                         this.el.un("contextmenu", this.showContextMenu, this);
41883                 }
41884         },
41885
41886         showContextMenu: function(e, item) {
41887         item = this.findItemFromChild(e.getTarget());
41888                 if (item) {
41889                         e.stopEvent();
41890                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
41891                         this.contextMenu.showAt(e.getXY());
41892             }
41893     },
41894
41895 /**
41896  *      Remove {@link Roo.data.Record}s at the specified indices.
41897  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
41898  */
41899     remove: function(selectedIndices) {
41900                 selectedIndices = [].concat(selectedIndices);
41901                 for (var i = 0; i < selectedIndices.length; i++) {
41902                         var rec = this.store.getAt(selectedIndices[i]);
41903                         this.store.remove(rec);
41904                 }
41905     },
41906
41907 /**
41908  *      Double click fires the event, but also, if this is draggable, and there is only one other
41909  *      related DropZone, it transfers the selected node.
41910  */
41911     onDblClick : function(e){
41912         var item = this.findItemFromChild(e.getTarget());
41913         if(item){
41914             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
41915                 return false;
41916             }
41917             if (this.dragGroup) {
41918                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
41919                     while (targets.indexOf(this.dropZone) > -1) {
41920                             targets.remove(this.dropZone);
41921                                 }
41922                     if (targets.length == 1) {
41923                                         this.dragZone.cachedTarget = null;
41924                         var el = Roo.get(targets[0].getEl());
41925                         var box = el.getBox(true);
41926                         targets[0].onNodeDrop(el.dom, {
41927                                 target: el.dom,
41928                                 xy: [box.x, box.y + box.height - 1]
41929                         }, null, this.getDragData(e));
41930                     }
41931                 }
41932         }
41933     },
41934     
41935     handleSelection: function(e) {
41936                 this.dragZone.cachedTarget = null;
41937         var item = this.findItemFromChild(e.getTarget());
41938         if (!item) {
41939                 this.clearSelections(true);
41940                 return;
41941         }
41942                 if (item && (this.multiSelect || this.singleSelect)){
41943                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
41944                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
41945                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
41946                                 this.unselect(item);
41947                         } else {
41948                                 this.select(item, this.multiSelect && e.ctrlKey);
41949                                 this.lastSelection = item;
41950                         }
41951                 }
41952     },
41953
41954     onItemClick : function(item, index, e){
41955                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
41956                         return false;
41957                 }
41958                 return true;
41959     },
41960
41961     unselect : function(nodeInfo, suppressEvent){
41962                 var node = this.getNode(nodeInfo);
41963                 if(node && this.isSelected(node)){
41964                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
41965                                 Roo.fly(node).removeClass(this.selectedClass);
41966                                 this.selections.remove(node);
41967                                 if(!suppressEvent){
41968                                         this.fireEvent("selectionchange", this, this.selections);
41969                                 }
41970                         }
41971                 }
41972     }
41973 });
41974 /*
41975  * Based on:
41976  * Ext JS Library 1.1.1
41977  * Copyright(c) 2006-2007, Ext JS, LLC.
41978  *
41979  * Originally Released Under LGPL - original licence link has changed is not relivant.
41980  *
41981  * Fork - LGPL
41982  * <script type="text/javascript">
41983  */
41984  
41985 /**
41986  * @class Roo.LayoutManager
41987  * @extends Roo.util.Observable
41988  * Base class for layout managers.
41989  */
41990 Roo.LayoutManager = function(container, config){
41991     Roo.LayoutManager.superclass.constructor.call(this);
41992     this.el = Roo.get(container);
41993     // ie scrollbar fix
41994     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
41995         document.body.scroll = "no";
41996     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
41997         this.el.position('relative');
41998     }
41999     this.id = this.el.id;
42000     this.el.addClass("x-layout-container");
42001     /** false to disable window resize monitoring @type Boolean */
42002     this.monitorWindowResize = true;
42003     this.regions = {};
42004     this.addEvents({
42005         /**
42006          * @event layout
42007          * Fires when a layout is performed. 
42008          * @param {Roo.LayoutManager} this
42009          */
42010         "layout" : true,
42011         /**
42012          * @event regionresized
42013          * Fires when the user resizes a region. 
42014          * @param {Roo.LayoutRegion} region The resized region
42015          * @param {Number} newSize The new size (width for east/west, height for north/south)
42016          */
42017         "regionresized" : true,
42018         /**
42019          * @event regioncollapsed
42020          * Fires when a region is collapsed. 
42021          * @param {Roo.LayoutRegion} region The collapsed region
42022          */
42023         "regioncollapsed" : true,
42024         /**
42025          * @event regionexpanded
42026          * Fires when a region is expanded.  
42027          * @param {Roo.LayoutRegion} region The expanded region
42028          */
42029         "regionexpanded" : true
42030     });
42031     this.updating = false;
42032     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42033 };
42034
42035 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42036     /**
42037      * Returns true if this layout is currently being updated
42038      * @return {Boolean}
42039      */
42040     isUpdating : function(){
42041         return this.updating; 
42042     },
42043     
42044     /**
42045      * Suspend the LayoutManager from doing auto-layouts while
42046      * making multiple add or remove calls
42047      */
42048     beginUpdate : function(){
42049         this.updating = true;    
42050     },
42051     
42052     /**
42053      * Restore auto-layouts and optionally disable the manager from performing a layout
42054      * @param {Boolean} noLayout true to disable a layout update 
42055      */
42056     endUpdate : function(noLayout){
42057         this.updating = false;
42058         if(!noLayout){
42059             this.layout();
42060         }    
42061     },
42062     
42063     layout: function(){
42064         
42065     },
42066     
42067     onRegionResized : function(region, newSize){
42068         this.fireEvent("regionresized", region, newSize);
42069         this.layout();
42070     },
42071     
42072     onRegionCollapsed : function(region){
42073         this.fireEvent("regioncollapsed", region);
42074     },
42075     
42076     onRegionExpanded : function(region){
42077         this.fireEvent("regionexpanded", region);
42078     },
42079         
42080     /**
42081      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42082      * performs box-model adjustments.
42083      * @return {Object} The size as an object {width: (the width), height: (the height)}
42084      */
42085     getViewSize : function(){
42086         var size;
42087         if(this.el.dom != document.body){
42088             size = this.el.getSize();
42089         }else{
42090             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42091         }
42092         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42093         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42094         return size;
42095     },
42096     
42097     /**
42098      * Returns the Element this layout is bound to.
42099      * @return {Roo.Element}
42100      */
42101     getEl : function(){
42102         return this.el;
42103     },
42104     
42105     /**
42106      * Returns the specified region.
42107      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42108      * @return {Roo.LayoutRegion}
42109      */
42110     getRegion : function(target){
42111         return this.regions[target.toLowerCase()];
42112     },
42113     
42114     onWindowResize : function(){
42115         if(this.monitorWindowResize){
42116             this.layout();
42117         }
42118     }
42119 });/*
42120  * Based on:
42121  * Ext JS Library 1.1.1
42122  * Copyright(c) 2006-2007, Ext JS, LLC.
42123  *
42124  * Originally Released Under LGPL - original licence link has changed is not relivant.
42125  *
42126  * Fork - LGPL
42127  * <script type="text/javascript">
42128  */
42129 /**
42130  * @class Roo.BorderLayout
42131  * @extends Roo.LayoutManager
42132  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42133  * please see: <br><br>
42134  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
42135  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
42136  * Example:
42137  <pre><code>
42138  var layout = new Roo.BorderLayout(document.body, {
42139     north: {
42140         initialSize: 25,
42141         titlebar: false
42142     },
42143     west: {
42144         split:true,
42145         initialSize: 200,
42146         minSize: 175,
42147         maxSize: 400,
42148         titlebar: true,
42149         collapsible: true
42150     },
42151     east: {
42152         split:true,
42153         initialSize: 202,
42154         minSize: 175,
42155         maxSize: 400,
42156         titlebar: true,
42157         collapsible: true
42158     },
42159     south: {
42160         split:true,
42161         initialSize: 100,
42162         minSize: 100,
42163         maxSize: 200,
42164         titlebar: true,
42165         collapsible: true
42166     },
42167     center: {
42168         titlebar: true,
42169         autoScroll:true,
42170         resizeTabs: true,
42171         minTabWidth: 50,
42172         preferredTabWidth: 150
42173     }
42174 });
42175
42176 // shorthand
42177 var CP = Roo.ContentPanel;
42178
42179 layout.beginUpdate();
42180 layout.add("north", new CP("north", "North"));
42181 layout.add("south", new CP("south", {title: "South", closable: true}));
42182 layout.add("west", new CP("west", {title: "West"}));
42183 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42184 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42185 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42186 layout.getRegion("center").showPanel("center1");
42187 layout.endUpdate();
42188 </code></pre>
42189
42190 <b>The container the layout is rendered into can be either the body element or any other element.
42191 If it is not the body element, the container needs to either be an absolute positioned element,
42192 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42193 the container size if it is not the body element.</b>
42194
42195 * @constructor
42196 * Create a new BorderLayout
42197 * @param {String/HTMLElement/Element} container The container this layout is bound to
42198 * @param {Object} config Configuration options
42199  */
42200 Roo.BorderLayout = function(container, config){
42201     config = config || {};
42202     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42203     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42204     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42205         var target = this.factory.validRegions[i];
42206         if(config[target]){
42207             this.addRegion(target, config[target]);
42208         }
42209     }
42210 };
42211
42212 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42213     /**
42214      * Creates and adds a new region if it doesn't already exist.
42215      * @param {String} target The target region key (north, south, east, west or center).
42216      * @param {Object} config The regions config object
42217      * @return {BorderLayoutRegion} The new region
42218      */
42219     addRegion : function(target, config){
42220         if(!this.regions[target]){
42221             var r = this.factory.create(target, this, config);
42222             this.bindRegion(target, r);
42223         }
42224         return this.regions[target];
42225     },
42226
42227     // private (kinda)
42228     bindRegion : function(name, r){
42229         this.regions[name] = r;
42230         r.on("visibilitychange", this.layout, this);
42231         r.on("paneladded", this.layout, this);
42232         r.on("panelremoved", this.layout, this);
42233         r.on("invalidated", this.layout, this);
42234         r.on("resized", this.onRegionResized, this);
42235         r.on("collapsed", this.onRegionCollapsed, this);
42236         r.on("expanded", this.onRegionExpanded, this);
42237     },
42238
42239     /**
42240      * Performs a layout update.
42241      */
42242     layout : function(){
42243         if(this.updating) return;
42244         var size = this.getViewSize();
42245         var w = size.width;
42246         var h = size.height;
42247         var centerW = w;
42248         var centerH = h;
42249         var centerY = 0;
42250         var centerX = 0;
42251         //var x = 0, y = 0;
42252
42253         var rs = this.regions;
42254         var north = rs["north"];
42255         var south = rs["south"]; 
42256         var west = rs["west"];
42257         var east = rs["east"];
42258         var center = rs["center"];
42259         //if(this.hideOnLayout){ // not supported anymore
42260             //c.el.setStyle("display", "none");
42261         //}
42262         if(north && north.isVisible()){
42263             var b = north.getBox();
42264             var m = north.getMargins();
42265             b.width = w - (m.left+m.right);
42266             b.x = m.left;
42267             b.y = m.top;
42268             centerY = b.height + b.y + m.bottom;
42269             centerH -= centerY;
42270             north.updateBox(this.safeBox(b));
42271         }
42272         if(south && south.isVisible()){
42273             var b = south.getBox();
42274             var m = south.getMargins();
42275             b.width = w - (m.left+m.right);
42276             b.x = m.left;
42277             var totalHeight = (b.height + m.top + m.bottom);
42278             b.y = h - totalHeight + m.top;
42279             centerH -= totalHeight;
42280             south.updateBox(this.safeBox(b));
42281         }
42282         if(west && west.isVisible()){
42283             var b = west.getBox();
42284             var m = west.getMargins();
42285             b.height = centerH - (m.top+m.bottom);
42286             b.x = m.left;
42287             b.y = centerY + m.top;
42288             var totalWidth = (b.width + m.left + m.right);
42289             centerX += totalWidth;
42290             centerW -= totalWidth;
42291             west.updateBox(this.safeBox(b));
42292         }
42293         if(east && east.isVisible()){
42294             var b = east.getBox();
42295             var m = east.getMargins();
42296             b.height = centerH - (m.top+m.bottom);
42297             var totalWidth = (b.width + m.left + m.right);
42298             b.x = w - totalWidth + m.left;
42299             b.y = centerY + m.top;
42300             centerW -= totalWidth;
42301             east.updateBox(this.safeBox(b));
42302         }
42303         if(center){
42304             var m = center.getMargins();
42305             var centerBox = {
42306                 x: centerX + m.left,
42307                 y: centerY + m.top,
42308                 width: centerW - (m.left+m.right),
42309                 height: centerH - (m.top+m.bottom)
42310             };
42311             //if(this.hideOnLayout){
42312                 //center.el.setStyle("display", "block");
42313             //}
42314             center.updateBox(this.safeBox(centerBox));
42315         }
42316         this.el.repaint();
42317         this.fireEvent("layout", this);
42318     },
42319
42320     // private
42321     safeBox : function(box){
42322         box.width = Math.max(0, box.width);
42323         box.height = Math.max(0, box.height);
42324         return box;
42325     },
42326
42327     /**
42328      * Adds a ContentPanel (or subclass) to this layout.
42329      * @param {String} target The target region key (north, south, east, west or center).
42330      * @param {Roo.ContentPanel} panel The panel to add
42331      * @return {Roo.ContentPanel} The added panel
42332      */
42333     add : function(target, panel){
42334          
42335         target = target.toLowerCase();
42336         return this.regions[target].add(panel);
42337     },
42338
42339     /**
42340      * Remove a ContentPanel (or subclass) to this layout.
42341      * @param {String} target The target region key (north, south, east, west or center).
42342      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42343      * @return {Roo.ContentPanel} The removed panel
42344      */
42345     remove : function(target, panel){
42346         target = target.toLowerCase();
42347         return this.regions[target].remove(panel);
42348     },
42349
42350     /**
42351      * Searches all regions for a panel with the specified id
42352      * @param {String} panelId
42353      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42354      */
42355     findPanel : function(panelId){
42356         var rs = this.regions;
42357         for(var target in rs){
42358             if(typeof rs[target] != "function"){
42359                 var p = rs[target].getPanel(panelId);
42360                 if(p){
42361                     return p;
42362                 }
42363             }
42364         }
42365         return null;
42366     },
42367
42368     /**
42369      * Searches all regions for a panel with the specified id and activates (shows) it.
42370      * @param {String/ContentPanel} panelId The panels id or the panel itself
42371      * @return {Roo.ContentPanel} The shown panel or null
42372      */
42373     showPanel : function(panelId) {
42374       var rs = this.regions;
42375       for(var target in rs){
42376          var r = rs[target];
42377          if(typeof r != "function"){
42378             if(r.hasPanel(panelId)){
42379                return r.showPanel(panelId);
42380             }
42381          }
42382       }
42383       return null;
42384    },
42385
42386    /**
42387      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42388      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42389      */
42390     restoreState : function(provider){
42391         if(!provider){
42392             provider = Roo.state.Manager;
42393         }
42394         var sm = new Roo.LayoutStateManager();
42395         sm.init(this, provider);
42396     },
42397
42398     /**
42399      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42400      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42401      * a valid ContentPanel config object.  Example:
42402      * <pre><code>
42403 // Create the main layout
42404 var layout = new Roo.BorderLayout('main-ct', {
42405     west: {
42406         split:true,
42407         minSize: 175,
42408         titlebar: true
42409     },
42410     center: {
42411         title:'Components'
42412     }
42413 }, 'main-ct');
42414
42415 // Create and add multiple ContentPanels at once via configs
42416 layout.batchAdd({
42417    west: {
42418        id: 'source-files',
42419        autoCreate:true,
42420        title:'Ext Source Files',
42421        autoScroll:true,
42422        fitToFrame:true
42423    },
42424    center : {
42425        el: cview,
42426        autoScroll:true,
42427        fitToFrame:true,
42428        toolbar: tb,
42429        resizeEl:'cbody'
42430    }
42431 });
42432 </code></pre>
42433      * @param {Object} regions An object containing ContentPanel configs by region name
42434      */
42435     batchAdd : function(regions){
42436         this.beginUpdate();
42437         for(var rname in regions){
42438             var lr = this.regions[rname];
42439             if(lr){
42440                 this.addTypedPanels(lr, regions[rname]);
42441             }
42442         }
42443         this.endUpdate();
42444     },
42445
42446     // private
42447     addTypedPanels : function(lr, ps){
42448         if(typeof ps == 'string'){
42449             lr.add(new Roo.ContentPanel(ps));
42450         }
42451         else if(ps instanceof Array){
42452             for(var i =0, len = ps.length; i < len; i++){
42453                 this.addTypedPanels(lr, ps[i]);
42454             }
42455         }
42456         else if(!ps.events){ // raw config?
42457             var el = ps.el;
42458             delete ps.el; // prevent conflict
42459             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42460         }
42461         else {  // panel object assumed!
42462             lr.add(ps);
42463         }
42464     },
42465     /**
42466      * Adds a xtype elements to the layout.
42467      * <pre><code>
42468
42469 layout.addxtype({
42470        xtype : 'ContentPanel',
42471        region: 'west',
42472        items: [ .... ]
42473    }
42474 );
42475
42476 layout.addxtype({
42477         xtype : 'NestedLayoutPanel',
42478         region: 'west',
42479         layout: {
42480            center: { },
42481            west: { }   
42482         },
42483         items : [ ... list of content panels or nested layout panels.. ]
42484    }
42485 );
42486 </code></pre>
42487      * @param {Object} cfg Xtype definition of item to add.
42488      */
42489     addxtype : function(cfg)
42490     {
42491         // basically accepts a pannel...
42492         // can accept a layout region..!?!?
42493        // console.log('BorderLayout add ' + cfg.xtype)
42494         
42495         if (!cfg.xtype.match(/Panel$/)) {
42496             return false;
42497         }
42498         var ret = false;
42499         var region = cfg.region;
42500         delete cfg.region;
42501         
42502           
42503         var xitems = [];
42504         if (cfg.items) {
42505             xitems = cfg.items;
42506             delete cfg.items;
42507         }
42508         
42509         
42510         switch(cfg.xtype) 
42511         {
42512             case 'ContentPanel':  // ContentPanel (el, cfg)
42513                 if(cfg.autoCreate) {
42514                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42515                 } else {
42516                     var el = this.el.createChild();
42517                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42518                 }
42519                 
42520                 this.add(region, ret);
42521                 break;
42522             
42523             
42524             case 'TreePanel': // our new panel!
42525                 cfg.el = this.el.createChild();
42526                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42527                 this.add(region, ret);
42528                 break;
42529             
42530             case 'NestedLayoutPanel': 
42531                 // create a new Layout (which is  a Border Layout...
42532                 var el = this.el.createChild();
42533                 var clayout = cfg.layout;
42534                 delete cfg.layout;
42535                 clayout.items   = clayout.items  || [];
42536                 // replace this exitems with the clayout ones..
42537                 xitems = clayout.items;
42538                  
42539                 
42540                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42541                     cfg.background = false;
42542                 }
42543                 var layout = new Roo.BorderLayout(el, clayout);
42544                 
42545                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42546                 //console.log('adding nested layout panel '  + cfg.toSource());
42547                 this.add(region, ret);
42548                 
42549                 break;
42550                 
42551             case 'GridPanel': 
42552             
42553                 // needs grid and region
42554                 
42555                 //var el = this.getRegion(region).el.createChild();
42556                 var el = this.el.createChild();
42557                 // create the grid first...
42558                 
42559                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42560                 delete cfg.grid;
42561                 if (region == 'center' && this.active ) {
42562                     cfg.background = false;
42563                 }
42564                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42565                 
42566                 this.add(region, ret);
42567                 if (cfg.background) {
42568                     ret.on('activate', function(gp) {
42569                         if (!gp.grid.rendered) {
42570                             gp.grid.render();
42571                         }
42572                     });
42573                 } else {
42574                     grid.render();
42575                 }
42576                 break;
42577            
42578                
42579                 
42580                 
42581             default: 
42582                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42583                 return;
42584              // GridPanel (grid, cfg)
42585             
42586         }
42587         this.beginUpdate();
42588         // add children..
42589         Roo.each(xitems, function(i)  {
42590             ret.addxtype(i);
42591         });
42592         this.endUpdate();
42593         return ret;
42594         
42595     }
42596 });
42597
42598 /**
42599  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42600  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42601  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42602  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42603  * <pre><code>
42604 // shorthand
42605 var CP = Roo.ContentPanel;
42606
42607 var layout = Roo.BorderLayout.create({
42608     north: {
42609         initialSize: 25,
42610         titlebar: false,
42611         panels: [new CP("north", "North")]
42612     },
42613     west: {
42614         split:true,
42615         initialSize: 200,
42616         minSize: 175,
42617         maxSize: 400,
42618         titlebar: true,
42619         collapsible: true,
42620         panels: [new CP("west", {title: "West"})]
42621     },
42622     east: {
42623         split:true,
42624         initialSize: 202,
42625         minSize: 175,
42626         maxSize: 400,
42627         titlebar: true,
42628         collapsible: true,
42629         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42630     },
42631     south: {
42632         split:true,
42633         initialSize: 100,
42634         minSize: 100,
42635         maxSize: 200,
42636         titlebar: true,
42637         collapsible: true,
42638         panels: [new CP("south", {title: "South", closable: true})]
42639     },
42640     center: {
42641         titlebar: true,
42642         autoScroll:true,
42643         resizeTabs: true,
42644         minTabWidth: 50,
42645         preferredTabWidth: 150,
42646         panels: [
42647             new CP("center1", {title: "Close Me", closable: true}),
42648             new CP("center2", {title: "Center Panel", closable: false})
42649         ]
42650     }
42651 }, document.body);
42652
42653 layout.getRegion("center").showPanel("center1");
42654 </code></pre>
42655  * @param config
42656  * @param targetEl
42657  */
42658 Roo.BorderLayout.create = function(config, targetEl){
42659     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42660     layout.beginUpdate();
42661     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42662     for(var j = 0, jlen = regions.length; j < jlen; j++){
42663         var lr = regions[j];
42664         if(layout.regions[lr] && config[lr].panels){
42665             var r = layout.regions[lr];
42666             var ps = config[lr].panels;
42667             layout.addTypedPanels(r, ps);
42668         }
42669     }
42670     layout.endUpdate();
42671     return layout;
42672 };
42673
42674 // private
42675 Roo.BorderLayout.RegionFactory = {
42676     // private
42677     validRegions : ["north","south","east","west","center"],
42678
42679     // private
42680     create : function(target, mgr, config){
42681         target = target.toLowerCase();
42682         if(config.lightweight || config.basic){
42683             return new Roo.BasicLayoutRegion(mgr, config, target);
42684         }
42685         switch(target){
42686             case "north":
42687                 return new Roo.NorthLayoutRegion(mgr, config);
42688             case "south":
42689                 return new Roo.SouthLayoutRegion(mgr, config);
42690             case "east":
42691                 return new Roo.EastLayoutRegion(mgr, config);
42692             case "west":
42693                 return new Roo.WestLayoutRegion(mgr, config);
42694             case "center":
42695                 return new Roo.CenterLayoutRegion(mgr, config);
42696         }
42697         throw 'Layout region "'+target+'" not supported.';
42698     }
42699 };/*
42700  * Based on:
42701  * Ext JS Library 1.1.1
42702  * Copyright(c) 2006-2007, Ext JS, LLC.
42703  *
42704  * Originally Released Under LGPL - original licence link has changed is not relivant.
42705  *
42706  * Fork - LGPL
42707  * <script type="text/javascript">
42708  */
42709  
42710 /**
42711  * @class Roo.BasicLayoutRegion
42712  * @extends Roo.util.Observable
42713  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42714  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42715  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42716  */
42717 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42718     this.mgr = mgr;
42719     this.position  = pos;
42720     this.events = {
42721         /**
42722          * @scope Roo.BasicLayoutRegion
42723          */
42724         
42725         /**
42726          * @event beforeremove
42727          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42728          * @param {Roo.LayoutRegion} this
42729          * @param {Roo.ContentPanel} panel The panel
42730          * @param {Object} e The cancel event object
42731          */
42732         "beforeremove" : true,
42733         /**
42734          * @event invalidated
42735          * Fires when the layout for this region is changed.
42736          * @param {Roo.LayoutRegion} this
42737          */
42738         "invalidated" : true,
42739         /**
42740          * @event visibilitychange
42741          * Fires when this region is shown or hidden 
42742          * @param {Roo.LayoutRegion} this
42743          * @param {Boolean} visibility true or false
42744          */
42745         "visibilitychange" : true,
42746         /**
42747          * @event paneladded
42748          * Fires when a panel is added. 
42749          * @param {Roo.LayoutRegion} this
42750          * @param {Roo.ContentPanel} panel The panel
42751          */
42752         "paneladded" : true,
42753         /**
42754          * @event panelremoved
42755          * Fires when a panel is removed. 
42756          * @param {Roo.LayoutRegion} this
42757          * @param {Roo.ContentPanel} panel The panel
42758          */
42759         "panelremoved" : true,
42760         /**
42761          * @event collapsed
42762          * Fires when this region is collapsed.
42763          * @param {Roo.LayoutRegion} this
42764          */
42765         "collapsed" : true,
42766         /**
42767          * @event expanded
42768          * Fires when this region is expanded.
42769          * @param {Roo.LayoutRegion} this
42770          */
42771         "expanded" : true,
42772         /**
42773          * @event slideshow
42774          * Fires when this region is slid into view.
42775          * @param {Roo.LayoutRegion} this
42776          */
42777         "slideshow" : true,
42778         /**
42779          * @event slidehide
42780          * Fires when this region slides out of view. 
42781          * @param {Roo.LayoutRegion} this
42782          */
42783         "slidehide" : true,
42784         /**
42785          * @event panelactivated
42786          * Fires when a panel is activated. 
42787          * @param {Roo.LayoutRegion} this
42788          * @param {Roo.ContentPanel} panel The activated panel
42789          */
42790         "panelactivated" : true,
42791         /**
42792          * @event resized
42793          * Fires when the user resizes this region. 
42794          * @param {Roo.LayoutRegion} this
42795          * @param {Number} newSize The new size (width for east/west, height for north/south)
42796          */
42797         "resized" : true
42798     };
42799     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42800     this.panels = new Roo.util.MixedCollection();
42801     this.panels.getKey = this.getPanelId.createDelegate(this);
42802     this.box = null;
42803     this.activePanel = null;
42804     // ensure listeners are added...
42805     
42806     if (config.listeners || config.events) {
42807         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
42808             listeners : config.listeners || {},
42809             events : config.events || {}
42810         });
42811     }
42812     
42813     if(skipConfig !== true){
42814         this.applyConfig(config);
42815     }
42816 };
42817
42818 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
42819     getPanelId : function(p){
42820         return p.getId();
42821     },
42822     
42823     applyConfig : function(config){
42824         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42825         this.config = config;
42826         
42827     },
42828     
42829     /**
42830      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42831      * the width, for horizontal (north, south) the height.
42832      * @param {Number} newSize The new width or height
42833      */
42834     resizeTo : function(newSize){
42835         var el = this.el ? this.el :
42836                  (this.activePanel ? this.activePanel.getEl() : null);
42837         if(el){
42838             switch(this.position){
42839                 case "east":
42840                 case "west":
42841                     el.setWidth(newSize);
42842                     this.fireEvent("resized", this, newSize);
42843                 break;
42844                 case "north":
42845                 case "south":
42846                     el.setHeight(newSize);
42847                     this.fireEvent("resized", this, newSize);
42848                 break;                
42849             }
42850         }
42851     },
42852     
42853     getBox : function(){
42854         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42855     },
42856     
42857     getMargins : function(){
42858         return this.margins;
42859     },
42860     
42861     updateBox : function(box){
42862         this.box = box;
42863         var el = this.activePanel.getEl();
42864         el.dom.style.left = box.x + "px";
42865         el.dom.style.top = box.y + "px";
42866         this.activePanel.setSize(box.width, box.height);
42867     },
42868     
42869     /**
42870      * Returns the container element for this region.
42871      * @return {Roo.Element}
42872      */
42873     getEl : function(){
42874         return this.activePanel;
42875     },
42876     
42877     /**
42878      * Returns true if this region is currently visible.
42879      * @return {Boolean}
42880      */
42881     isVisible : function(){
42882         return this.activePanel ? true : false;
42883     },
42884     
42885     setActivePanel : function(panel){
42886         panel = this.getPanel(panel);
42887         if(this.activePanel && this.activePanel != panel){
42888             this.activePanel.setActiveState(false);
42889             this.activePanel.getEl().setLeftTop(-10000,-10000);
42890         }
42891         this.activePanel = panel;
42892         panel.setActiveState(true);
42893         if(this.box){
42894             panel.setSize(this.box.width, this.box.height);
42895         }
42896         this.fireEvent("panelactivated", this, panel);
42897         this.fireEvent("invalidated");
42898     },
42899     
42900     /**
42901      * Show the specified panel.
42902      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42903      * @return {Roo.ContentPanel} The shown panel or null
42904      */
42905     showPanel : function(panel){
42906         if(panel = this.getPanel(panel)){
42907             this.setActivePanel(panel);
42908         }
42909         return panel;
42910     },
42911     
42912     /**
42913      * Get the active panel for this region.
42914      * @return {Roo.ContentPanel} The active panel or null
42915      */
42916     getActivePanel : function(){
42917         return this.activePanel;
42918     },
42919     
42920     /**
42921      * Add the passed ContentPanel(s)
42922      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42923      * @return {Roo.ContentPanel} The panel added (if only one was added)
42924      */
42925     add : function(panel){
42926         if(arguments.length > 1){
42927             for(var i = 0, len = arguments.length; i < len; i++) {
42928                 this.add(arguments[i]);
42929             }
42930             return null;
42931         }
42932         if(this.hasPanel(panel)){
42933             this.showPanel(panel);
42934             return panel;
42935         }
42936         var el = panel.getEl();
42937         if(el.dom.parentNode != this.mgr.el.dom){
42938             this.mgr.el.dom.appendChild(el.dom);
42939         }
42940         if(panel.setRegion){
42941             panel.setRegion(this);
42942         }
42943         this.panels.add(panel);
42944         el.setStyle("position", "absolute");
42945         if(!panel.background){
42946             this.setActivePanel(panel);
42947             if(this.config.initialSize && this.panels.getCount()==1){
42948                 this.resizeTo(this.config.initialSize);
42949             }
42950         }
42951         this.fireEvent("paneladded", this, panel);
42952         return panel;
42953     },
42954     
42955     /**
42956      * Returns true if the panel is in this region.
42957      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42958      * @return {Boolean}
42959      */
42960     hasPanel : function(panel){
42961         if(typeof panel == "object"){ // must be panel obj
42962             panel = panel.getId();
42963         }
42964         return this.getPanel(panel) ? true : false;
42965     },
42966     
42967     /**
42968      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42969      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42970      * @param {Boolean} preservePanel Overrides the config preservePanel option
42971      * @return {Roo.ContentPanel} The panel that was removed
42972      */
42973     remove : function(panel, preservePanel){
42974         panel = this.getPanel(panel);
42975         if(!panel){
42976             return null;
42977         }
42978         var e = {};
42979         this.fireEvent("beforeremove", this, panel, e);
42980         if(e.cancel === true){
42981             return null;
42982         }
42983         var panelId = panel.getId();
42984         this.panels.removeKey(panelId);
42985         return panel;
42986     },
42987     
42988     /**
42989      * Returns the panel specified or null if it's not in this region.
42990      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42991      * @return {Roo.ContentPanel}
42992      */
42993     getPanel : function(id){
42994         if(typeof id == "object"){ // must be panel obj
42995             return id;
42996         }
42997         return this.panels.get(id);
42998     },
42999     
43000     /**
43001      * Returns this regions position (north/south/east/west/center).
43002      * @return {String} 
43003      */
43004     getPosition: function(){
43005         return this.position;    
43006     }
43007 });/*
43008  * Based on:
43009  * Ext JS Library 1.1.1
43010  * Copyright(c) 2006-2007, Ext JS, LLC.
43011  *
43012  * Originally Released Under LGPL - original licence link has changed is not relivant.
43013  *
43014  * Fork - LGPL
43015  * <script type="text/javascript">
43016  */
43017  
43018 /**
43019  * @class Roo.LayoutRegion
43020  * @extends Roo.BasicLayoutRegion
43021  * This class represents a region in a layout manager.
43022  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43023  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43024  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43025  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43026  * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
43027  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43028  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43029  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43030  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43031  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43032  * @cfg {String} title The title for the region (overrides panel titles)
43033  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43034  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43035  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43036  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43037  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43038  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43039  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43040  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43041  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43042  * @cfg {Boolean} showPin True to show a pin button
43043 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43044 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43045 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43046 * @cfg {Number} width  For East/West panels
43047 * @cfg {Number} height For North/South panels
43048 * @cfg {Boolean} split To show the splitter
43049  */
43050 Roo.LayoutRegion = function(mgr, config, pos){
43051     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43052     var dh = Roo.DomHelper;
43053     /** This region's container element 
43054     * @type Roo.Element */
43055     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43056     /** This region's title element 
43057     * @type Roo.Element */
43058
43059     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43060         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43061         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43062     ]}, true);
43063     this.titleEl.enableDisplayMode();
43064     /** This region's title text element 
43065     * @type HTMLElement */
43066     this.titleTextEl = this.titleEl.dom.firstChild;
43067     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43068     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43069     this.closeBtn.enableDisplayMode();
43070     this.closeBtn.on("click", this.closeClicked, this);
43071     this.closeBtn.hide();
43072
43073     this.createBody(config);
43074     this.visible = true;
43075     this.collapsed = false;
43076
43077     if(config.hideWhenEmpty){
43078         this.hide();
43079         this.on("paneladded", this.validateVisibility, this);
43080         this.on("panelremoved", this.validateVisibility, this);
43081     }
43082     this.applyConfig(config);
43083 };
43084
43085 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43086
43087     createBody : function(){
43088         /** This region's body element 
43089         * @type Roo.Element */
43090         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43091     },
43092
43093     applyConfig : function(c){
43094         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43095             var dh = Roo.DomHelper;
43096             if(c.titlebar !== false){
43097                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43098                 this.collapseBtn.on("click", this.collapse, this);
43099                 this.collapseBtn.enableDisplayMode();
43100
43101                 if(c.showPin === true || this.showPin){
43102                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43103                     this.stickBtn.enableDisplayMode();
43104                     this.stickBtn.on("click", this.expand, this);
43105                     this.stickBtn.hide();
43106                 }
43107             }
43108             /** This region's collapsed element
43109             * @type Roo.Element */
43110             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43111                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43112             ]}, true);
43113             if(c.floatable !== false){
43114                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43115                this.collapsedEl.on("click", this.collapseClick, this);
43116             }
43117
43118             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43119                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43120                    id: "message", unselectable: "on", style:{"float":"left"}});
43121                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43122              }
43123             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43124             this.expandBtn.on("click", this.expand, this);
43125         }
43126         if(this.collapseBtn){
43127             this.collapseBtn.setVisible(c.collapsible == true);
43128         }
43129         this.cmargins = c.cmargins || this.cmargins ||
43130                          (this.position == "west" || this.position == "east" ?
43131                              {top: 0, left: 2, right:2, bottom: 0} :
43132                              {top: 2, left: 0, right:0, bottom: 2});
43133         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43134         this.bottomTabs = c.tabPosition != "top";
43135         this.autoScroll = c.autoScroll || false;
43136         if(this.autoScroll){
43137             this.bodyEl.setStyle("overflow", "auto");
43138         }else{
43139             this.bodyEl.setStyle("overflow", "hidden");
43140         }
43141         //if(c.titlebar !== false){
43142             if((!c.titlebar && !c.title) || c.titlebar === false){
43143                 this.titleEl.hide();
43144             }else{
43145                 this.titleEl.show();
43146                 if(c.title){
43147                     this.titleTextEl.innerHTML = c.title;
43148                 }
43149             }
43150         //}
43151         this.duration = c.duration || .30;
43152         this.slideDuration = c.slideDuration || .45;
43153         this.config = c;
43154         if(c.collapsed){
43155             this.collapse(true);
43156         }
43157         if(c.hidden){
43158             this.hide();
43159         }
43160     },
43161     /**
43162      * Returns true if this region is currently visible.
43163      * @return {Boolean}
43164      */
43165     isVisible : function(){
43166         return this.visible;
43167     },
43168
43169     /**
43170      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43171      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43172      */
43173     setCollapsedTitle : function(title){
43174         title = title || "&#160;";
43175         if(this.collapsedTitleTextEl){
43176             this.collapsedTitleTextEl.innerHTML = title;
43177         }
43178     },
43179
43180     getBox : function(){
43181         var b;
43182         if(!this.collapsed){
43183             b = this.el.getBox(false, true);
43184         }else{
43185             b = this.collapsedEl.getBox(false, true);
43186         }
43187         return b;
43188     },
43189
43190     getMargins : function(){
43191         return this.collapsed ? this.cmargins : this.margins;
43192     },
43193
43194     highlight : function(){
43195         this.el.addClass("x-layout-panel-dragover");
43196     },
43197
43198     unhighlight : function(){
43199         this.el.removeClass("x-layout-panel-dragover");
43200     },
43201
43202     updateBox : function(box){
43203         this.box = box;
43204         if(!this.collapsed){
43205             this.el.dom.style.left = box.x + "px";
43206             this.el.dom.style.top = box.y + "px";
43207             this.updateBody(box.width, box.height);
43208         }else{
43209             this.collapsedEl.dom.style.left = box.x + "px";
43210             this.collapsedEl.dom.style.top = box.y + "px";
43211             this.collapsedEl.setSize(box.width, box.height);
43212         }
43213         if(this.tabs){
43214             this.tabs.autoSizeTabs();
43215         }
43216     },
43217
43218     updateBody : function(w, h){
43219         if(w !== null){
43220             this.el.setWidth(w);
43221             w -= this.el.getBorderWidth("rl");
43222             if(this.config.adjustments){
43223                 w += this.config.adjustments[0];
43224             }
43225         }
43226         if(h !== null){
43227             this.el.setHeight(h);
43228             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43229             h -= this.el.getBorderWidth("tb");
43230             if(this.config.adjustments){
43231                 h += this.config.adjustments[1];
43232             }
43233             this.bodyEl.setHeight(h);
43234             if(this.tabs){
43235                 h = this.tabs.syncHeight(h);
43236             }
43237         }
43238         if(this.panelSize){
43239             w = w !== null ? w : this.panelSize.width;
43240             h = h !== null ? h : this.panelSize.height;
43241         }
43242         if(this.activePanel){
43243             var el = this.activePanel.getEl();
43244             w = w !== null ? w : el.getWidth();
43245             h = h !== null ? h : el.getHeight();
43246             this.panelSize = {width: w, height: h};
43247             this.activePanel.setSize(w, h);
43248         }
43249         if(Roo.isIE && this.tabs){
43250             this.tabs.el.repaint();
43251         }
43252     },
43253
43254     /**
43255      * Returns the container element for this region.
43256      * @return {Roo.Element}
43257      */
43258     getEl : function(){
43259         return this.el;
43260     },
43261
43262     /**
43263      * Hides this region.
43264      */
43265     hide : function(){
43266         if(!this.collapsed){
43267             this.el.dom.style.left = "-2000px";
43268             this.el.hide();
43269         }else{
43270             this.collapsedEl.dom.style.left = "-2000px";
43271             this.collapsedEl.hide();
43272         }
43273         this.visible = false;
43274         this.fireEvent("visibilitychange", this, false);
43275     },
43276
43277     /**
43278      * Shows this region if it was previously hidden.
43279      */
43280     show : function(){
43281         if(!this.collapsed){
43282             this.el.show();
43283         }else{
43284             this.collapsedEl.show();
43285         }
43286         this.visible = true;
43287         this.fireEvent("visibilitychange", this, true);
43288     },
43289
43290     closeClicked : function(){
43291         if(this.activePanel){
43292             this.remove(this.activePanel);
43293         }
43294     },
43295
43296     collapseClick : function(e){
43297         if(this.isSlid){
43298            e.stopPropagation();
43299            this.slideIn();
43300         }else{
43301            e.stopPropagation();
43302            this.slideOut();
43303         }
43304     },
43305
43306     /**
43307      * Collapses this region.
43308      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43309      */
43310     collapse : function(skipAnim){
43311         if(this.collapsed) return;
43312         this.collapsed = true;
43313         if(this.split){
43314             this.split.el.hide();
43315         }
43316         if(this.config.animate && skipAnim !== true){
43317             this.fireEvent("invalidated", this);
43318             this.animateCollapse();
43319         }else{
43320             this.el.setLocation(-20000,-20000);
43321             this.el.hide();
43322             this.collapsedEl.show();
43323             this.fireEvent("collapsed", this);
43324             this.fireEvent("invalidated", this);
43325         }
43326     },
43327
43328     animateCollapse : function(){
43329         // overridden
43330     },
43331
43332     /**
43333      * Expands this region if it was previously collapsed.
43334      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43335      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43336      */
43337     expand : function(e, skipAnim){
43338         if(e) e.stopPropagation();
43339         if(!this.collapsed || this.el.hasActiveFx()) return;
43340         if(this.isSlid){
43341             this.afterSlideIn();
43342             skipAnim = true;
43343         }
43344         this.collapsed = false;
43345         if(this.config.animate && skipAnim !== true){
43346             this.animateExpand();
43347         }else{
43348             this.el.show();
43349             if(this.split){
43350                 this.split.el.show();
43351             }
43352             this.collapsedEl.setLocation(-2000,-2000);
43353             this.collapsedEl.hide();
43354             this.fireEvent("invalidated", this);
43355             this.fireEvent("expanded", this);
43356         }
43357     },
43358
43359     animateExpand : function(){
43360         // overridden
43361     },
43362
43363     initTabs : function(){
43364         this.bodyEl.setStyle("overflow", "hidden");
43365         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43366             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43367             disableTooltips: this.config.disableTabTips
43368         });
43369         if(this.config.hideTabs){
43370             ts.stripWrap.setDisplayed(false);
43371         }
43372         this.tabs = ts;
43373         ts.resizeTabs = this.config.resizeTabs === true;
43374         ts.minTabWidth = this.config.minTabWidth || 40;
43375         ts.maxTabWidth = this.config.maxTabWidth || 250;
43376         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43377         ts.monitorResize = false;
43378         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43379         ts.bodyEl.addClass('x-layout-tabs-body');
43380         this.panels.each(this.initPanelAsTab, this);
43381     },
43382
43383     initPanelAsTab : function(panel){
43384         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43385                     this.config.closeOnTab && panel.isClosable());
43386         if(panel.tabTip !== undefined){
43387             ti.setTooltip(panel.tabTip);
43388         }
43389         ti.on("activate", function(){
43390               this.setActivePanel(panel);
43391         }, this);
43392         if(this.config.closeOnTab){
43393             ti.on("beforeclose", function(t, e){
43394                 e.cancel = true;
43395                 this.remove(panel);
43396             }, this);
43397         }
43398         return ti;
43399     },
43400
43401     updatePanelTitle : function(panel, title){
43402         if(this.activePanel == panel){
43403             this.updateTitle(title);
43404         }
43405         if(this.tabs){
43406             var ti = this.tabs.getTab(panel.getEl().id);
43407             ti.setText(title);
43408             if(panel.tabTip !== undefined){
43409                 ti.setTooltip(panel.tabTip);
43410             }
43411         }
43412     },
43413
43414     updateTitle : function(title){
43415         if(this.titleTextEl && !this.config.title){
43416             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43417         }
43418     },
43419
43420     setActivePanel : function(panel){
43421         panel = this.getPanel(panel);
43422         if(this.activePanel && this.activePanel != panel){
43423             this.activePanel.setActiveState(false);
43424         }
43425         this.activePanel = panel;
43426         panel.setActiveState(true);
43427         if(this.panelSize){
43428             panel.setSize(this.panelSize.width, this.panelSize.height);
43429         }
43430         if(this.closeBtn){
43431             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43432         }
43433         this.updateTitle(panel.getTitle());
43434         if(this.tabs){
43435             this.fireEvent("invalidated", this);
43436         }
43437         this.fireEvent("panelactivated", this, panel);
43438     },
43439
43440     /**
43441      * Shows the specified panel.
43442      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43443      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43444      */
43445     showPanel : function(panel){
43446         if(panel = this.getPanel(panel)){
43447             if(this.tabs){
43448                 var tab = this.tabs.getTab(panel.getEl().id);
43449                 if(tab.isHidden()){
43450                     this.tabs.unhideTab(tab.id);
43451                 }
43452                 tab.activate();
43453             }else{
43454                 this.setActivePanel(panel);
43455             }
43456         }
43457         return panel;
43458     },
43459
43460     /**
43461      * Get the active panel for this region.
43462      * @return {Roo.ContentPanel} The active panel or null
43463      */
43464     getActivePanel : function(){
43465         return this.activePanel;
43466     },
43467
43468     validateVisibility : function(){
43469         if(this.panels.getCount() < 1){
43470             this.updateTitle("&#160;");
43471             this.closeBtn.hide();
43472             this.hide();
43473         }else{
43474             if(!this.isVisible()){
43475                 this.show();
43476             }
43477         }
43478     },
43479
43480     /**
43481      * Adds the passed ContentPanel(s) to this region.
43482      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43483      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43484      */
43485     add : function(panel){
43486         if(arguments.length > 1){
43487             for(var i = 0, len = arguments.length; i < len; i++) {
43488                 this.add(arguments[i]);
43489             }
43490             return null;
43491         }
43492         if(this.hasPanel(panel)){
43493             this.showPanel(panel);
43494             return panel;
43495         }
43496         panel.setRegion(this);
43497         this.panels.add(panel);
43498         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43499             this.bodyEl.dom.appendChild(panel.getEl().dom);
43500             if(panel.background !== true){
43501                 this.setActivePanel(panel);
43502             }
43503             this.fireEvent("paneladded", this, panel);
43504             return panel;
43505         }
43506         if(!this.tabs){
43507             this.initTabs();
43508         }else{
43509             this.initPanelAsTab(panel);
43510         }
43511         if(panel.background !== true){
43512             this.tabs.activate(panel.getEl().id);
43513         }
43514         this.fireEvent("paneladded", this, panel);
43515         return panel;
43516     },
43517
43518     /**
43519      * Hides the tab for the specified panel.
43520      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43521      */
43522     hidePanel : function(panel){
43523         if(this.tabs && (panel = this.getPanel(panel))){
43524             this.tabs.hideTab(panel.getEl().id);
43525         }
43526     },
43527
43528     /**
43529      * Unhides the tab for a previously hidden panel.
43530      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43531      */
43532     unhidePanel : function(panel){
43533         if(this.tabs && (panel = this.getPanel(panel))){
43534             this.tabs.unhideTab(panel.getEl().id);
43535         }
43536     },
43537
43538     clearPanels : function(){
43539         while(this.panels.getCount() > 0){
43540              this.remove(this.panels.first());
43541         }
43542     },
43543
43544     /**
43545      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43546      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43547      * @param {Boolean} preservePanel Overrides the config preservePanel option
43548      * @return {Roo.ContentPanel} The panel that was removed
43549      */
43550     remove : function(panel, preservePanel){
43551         panel = this.getPanel(panel);
43552         if(!panel){
43553             return null;
43554         }
43555         var e = {};
43556         this.fireEvent("beforeremove", this, panel, e);
43557         if(e.cancel === true){
43558             return null;
43559         }
43560         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43561         var panelId = panel.getId();
43562         this.panels.removeKey(panelId);
43563         if(preservePanel){
43564             document.body.appendChild(panel.getEl().dom);
43565         }
43566         if(this.tabs){
43567             this.tabs.removeTab(panel.getEl().id);
43568         }else if (!preservePanel){
43569             this.bodyEl.dom.removeChild(panel.getEl().dom);
43570         }
43571         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43572             var p = this.panels.first();
43573             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43574             tempEl.appendChild(p.getEl().dom);
43575             this.bodyEl.update("");
43576             this.bodyEl.dom.appendChild(p.getEl().dom);
43577             tempEl = null;
43578             this.updateTitle(p.getTitle());
43579             this.tabs = null;
43580             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43581             this.setActivePanel(p);
43582         }
43583         panel.setRegion(null);
43584         if(this.activePanel == panel){
43585             this.activePanel = null;
43586         }
43587         if(this.config.autoDestroy !== false && preservePanel !== true){
43588             try{panel.destroy();}catch(e){}
43589         }
43590         this.fireEvent("panelremoved", this, panel);
43591         return panel;
43592     },
43593
43594     /**
43595      * Returns the TabPanel component used by this region
43596      * @return {Roo.TabPanel}
43597      */
43598     getTabs : function(){
43599         return this.tabs;
43600     },
43601
43602     createTool : function(parentEl, className){
43603         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43604             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43605         btn.addClassOnOver("x-layout-tools-button-over");
43606         return btn;
43607     }
43608 });/*
43609  * Based on:
43610  * Ext JS Library 1.1.1
43611  * Copyright(c) 2006-2007, Ext JS, LLC.
43612  *
43613  * Originally Released Under LGPL - original licence link has changed is not relivant.
43614  *
43615  * Fork - LGPL
43616  * <script type="text/javascript">
43617  */
43618  
43619
43620
43621 /**
43622  * @class Roo.SplitLayoutRegion
43623  * @extends Roo.LayoutRegion
43624  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43625  */
43626 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43627     this.cursor = cursor;
43628     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43629 };
43630
43631 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43632     splitTip : "Drag to resize.",
43633     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43634     useSplitTips : false,
43635
43636     applyConfig : function(config){
43637         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43638         if(config.split){
43639             if(!this.split){
43640                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43641                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43642                 /** The SplitBar for this region 
43643                 * @type Roo.SplitBar */
43644                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43645                 this.split.on("moved", this.onSplitMove, this);
43646                 this.split.useShim = config.useShim === true;
43647                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43648                 if(this.useSplitTips){
43649                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43650                 }
43651                 if(config.collapsible){
43652                     this.split.el.on("dblclick", this.collapse,  this);
43653                 }
43654             }
43655             if(typeof config.minSize != "undefined"){
43656                 this.split.minSize = config.minSize;
43657             }
43658             if(typeof config.maxSize != "undefined"){
43659                 this.split.maxSize = config.maxSize;
43660             }
43661             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43662                 this.hideSplitter();
43663             }
43664         }
43665     },
43666
43667     getHMaxSize : function(){
43668          var cmax = this.config.maxSize || 10000;
43669          var center = this.mgr.getRegion("center");
43670          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43671     },
43672
43673     getVMaxSize : function(){
43674          var cmax = this.config.maxSize || 10000;
43675          var center = this.mgr.getRegion("center");
43676          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43677     },
43678
43679     onSplitMove : function(split, newSize){
43680         this.fireEvent("resized", this, newSize);
43681     },
43682     
43683     /** 
43684      * Returns the {@link Roo.SplitBar} for this region.
43685      * @return {Roo.SplitBar}
43686      */
43687     getSplitBar : function(){
43688         return this.split;
43689     },
43690     
43691     hide : function(){
43692         this.hideSplitter();
43693         Roo.SplitLayoutRegion.superclass.hide.call(this);
43694     },
43695
43696     hideSplitter : function(){
43697         if(this.split){
43698             this.split.el.setLocation(-2000,-2000);
43699             this.split.el.hide();
43700         }
43701     },
43702
43703     show : function(){
43704         if(this.split){
43705             this.split.el.show();
43706         }
43707         Roo.SplitLayoutRegion.superclass.show.call(this);
43708     },
43709     
43710     beforeSlide: function(){
43711         if(Roo.isGecko){// firefox overflow auto bug workaround
43712             this.bodyEl.clip();
43713             if(this.tabs) this.tabs.bodyEl.clip();
43714             if(this.activePanel){
43715                 this.activePanel.getEl().clip();
43716                 
43717                 if(this.activePanel.beforeSlide){
43718                     this.activePanel.beforeSlide();
43719                 }
43720             }
43721         }
43722     },
43723     
43724     afterSlide : function(){
43725         if(Roo.isGecko){// firefox overflow auto bug workaround
43726             this.bodyEl.unclip();
43727             if(this.tabs) this.tabs.bodyEl.unclip();
43728             if(this.activePanel){
43729                 this.activePanel.getEl().unclip();
43730                 if(this.activePanel.afterSlide){
43731                     this.activePanel.afterSlide();
43732                 }
43733             }
43734         }
43735     },
43736
43737     initAutoHide : function(){
43738         if(this.autoHide !== false){
43739             if(!this.autoHideHd){
43740                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43741                 this.autoHideHd = {
43742                     "mouseout": function(e){
43743                         if(!e.within(this.el, true)){
43744                             st.delay(500);
43745                         }
43746                     },
43747                     "mouseover" : function(e){
43748                         st.cancel();
43749                     },
43750                     scope : this
43751                 };
43752             }
43753             this.el.on(this.autoHideHd);
43754         }
43755     },
43756
43757     clearAutoHide : function(){
43758         if(this.autoHide !== false){
43759             this.el.un("mouseout", this.autoHideHd.mouseout);
43760             this.el.un("mouseover", this.autoHideHd.mouseover);
43761         }
43762     },
43763
43764     clearMonitor : function(){
43765         Roo.get(document).un("click", this.slideInIf, this);
43766     },
43767
43768     // these names are backwards but not changed for compat
43769     slideOut : function(){
43770         if(this.isSlid || this.el.hasActiveFx()){
43771             return;
43772         }
43773         this.isSlid = true;
43774         if(this.collapseBtn){
43775             this.collapseBtn.hide();
43776         }
43777         this.closeBtnState = this.closeBtn.getStyle('display');
43778         this.closeBtn.hide();
43779         if(this.stickBtn){
43780             this.stickBtn.show();
43781         }
43782         this.el.show();
43783         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43784         this.beforeSlide();
43785         this.el.setStyle("z-index", 10001);
43786         this.el.slideIn(this.getSlideAnchor(), {
43787             callback: function(){
43788                 this.afterSlide();
43789                 this.initAutoHide();
43790                 Roo.get(document).on("click", this.slideInIf, this);
43791                 this.fireEvent("slideshow", this);
43792             },
43793             scope: this,
43794             block: true
43795         });
43796     },
43797
43798     afterSlideIn : function(){
43799         this.clearAutoHide();
43800         this.isSlid = false;
43801         this.clearMonitor();
43802         this.el.setStyle("z-index", "");
43803         if(this.collapseBtn){
43804             this.collapseBtn.show();
43805         }
43806         this.closeBtn.setStyle('display', this.closeBtnState);
43807         if(this.stickBtn){
43808             this.stickBtn.hide();
43809         }
43810         this.fireEvent("slidehide", this);
43811     },
43812
43813     slideIn : function(cb){
43814         if(!this.isSlid || this.el.hasActiveFx()){
43815             Roo.callback(cb);
43816             return;
43817         }
43818         this.isSlid = false;
43819         this.beforeSlide();
43820         this.el.slideOut(this.getSlideAnchor(), {
43821             callback: function(){
43822                 this.el.setLeftTop(-10000, -10000);
43823                 this.afterSlide();
43824                 this.afterSlideIn();
43825                 Roo.callback(cb);
43826             },
43827             scope: this,
43828             block: true
43829         });
43830     },
43831     
43832     slideInIf : function(e){
43833         if(!e.within(this.el)){
43834             this.slideIn();
43835         }
43836     },
43837
43838     animateCollapse : function(){
43839         this.beforeSlide();
43840         this.el.setStyle("z-index", 20000);
43841         var anchor = this.getSlideAnchor();
43842         this.el.slideOut(anchor, {
43843             callback : function(){
43844                 this.el.setStyle("z-index", "");
43845                 this.collapsedEl.slideIn(anchor, {duration:.3});
43846                 this.afterSlide();
43847                 this.el.setLocation(-10000,-10000);
43848                 this.el.hide();
43849                 this.fireEvent("collapsed", this);
43850             },
43851             scope: this,
43852             block: true
43853         });
43854     },
43855
43856     animateExpand : function(){
43857         this.beforeSlide();
43858         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
43859         this.el.setStyle("z-index", 20000);
43860         this.collapsedEl.hide({
43861             duration:.1
43862         });
43863         this.el.slideIn(this.getSlideAnchor(), {
43864             callback : function(){
43865                 this.el.setStyle("z-index", "");
43866                 this.afterSlide();
43867                 if(this.split){
43868                     this.split.el.show();
43869                 }
43870                 this.fireEvent("invalidated", this);
43871                 this.fireEvent("expanded", this);
43872             },
43873             scope: this,
43874             block: true
43875         });
43876     },
43877
43878     anchors : {
43879         "west" : "left",
43880         "east" : "right",
43881         "north" : "top",
43882         "south" : "bottom"
43883     },
43884
43885     sanchors : {
43886         "west" : "l",
43887         "east" : "r",
43888         "north" : "t",
43889         "south" : "b"
43890     },
43891
43892     canchors : {
43893         "west" : "tl-tr",
43894         "east" : "tr-tl",
43895         "north" : "tl-bl",
43896         "south" : "bl-tl"
43897     },
43898
43899     getAnchor : function(){
43900         return this.anchors[this.position];
43901     },
43902
43903     getCollapseAnchor : function(){
43904         return this.canchors[this.position];
43905     },
43906
43907     getSlideAnchor : function(){
43908         return this.sanchors[this.position];
43909     },
43910
43911     getAlignAdj : function(){
43912         var cm = this.cmargins;
43913         switch(this.position){
43914             case "west":
43915                 return [0, 0];
43916             break;
43917             case "east":
43918                 return [0, 0];
43919             break;
43920             case "north":
43921                 return [0, 0];
43922             break;
43923             case "south":
43924                 return [0, 0];
43925             break;
43926         }
43927     },
43928
43929     getExpandAdj : function(){
43930         var c = this.collapsedEl, cm = this.cmargins;
43931         switch(this.position){
43932             case "west":
43933                 return [-(cm.right+c.getWidth()+cm.left), 0];
43934             break;
43935             case "east":
43936                 return [cm.right+c.getWidth()+cm.left, 0];
43937             break;
43938             case "north":
43939                 return [0, -(cm.top+cm.bottom+c.getHeight())];
43940             break;
43941             case "south":
43942                 return [0, cm.top+cm.bottom+c.getHeight()];
43943             break;
43944         }
43945     }
43946 });/*
43947  * Based on:
43948  * Ext JS Library 1.1.1
43949  * Copyright(c) 2006-2007, Ext JS, LLC.
43950  *
43951  * Originally Released Under LGPL - original licence link has changed is not relivant.
43952  *
43953  * Fork - LGPL
43954  * <script type="text/javascript">
43955  */
43956 /*
43957  * These classes are private internal classes
43958  */
43959 Roo.CenterLayoutRegion = function(mgr, config){
43960     Roo.LayoutRegion.call(this, mgr, config, "center");
43961     this.visible = true;
43962     this.minWidth = config.minWidth || 20;
43963     this.minHeight = config.minHeight || 20;
43964 };
43965
43966 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
43967     hide : function(){
43968         // center panel can't be hidden
43969     },
43970     
43971     show : function(){
43972         // center panel can't be hidden
43973     },
43974     
43975     getMinWidth: function(){
43976         return this.minWidth;
43977     },
43978     
43979     getMinHeight: function(){
43980         return this.minHeight;
43981     }
43982 });
43983
43984
43985 Roo.NorthLayoutRegion = function(mgr, config){
43986     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
43987     if(this.split){
43988         this.split.placement = Roo.SplitBar.TOP;
43989         this.split.orientation = Roo.SplitBar.VERTICAL;
43990         this.split.el.addClass("x-layout-split-v");
43991     }
43992     var size = config.initialSize || config.height;
43993     if(typeof size != "undefined"){
43994         this.el.setHeight(size);
43995     }
43996 };
43997 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
43998     orientation: Roo.SplitBar.VERTICAL,
43999     getBox : function(){
44000         if(this.collapsed){
44001             return this.collapsedEl.getBox();
44002         }
44003         var box = this.el.getBox();
44004         if(this.split){
44005             box.height += this.split.el.getHeight();
44006         }
44007         return box;
44008     },
44009     
44010     updateBox : function(box){
44011         if(this.split && !this.collapsed){
44012             box.height -= this.split.el.getHeight();
44013             this.split.el.setLeft(box.x);
44014             this.split.el.setTop(box.y+box.height);
44015             this.split.el.setWidth(box.width);
44016         }
44017         if(this.collapsed){
44018             this.updateBody(box.width, null);
44019         }
44020         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44021     }
44022 });
44023
44024 Roo.SouthLayoutRegion = function(mgr, config){
44025     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44026     if(this.split){
44027         this.split.placement = Roo.SplitBar.BOTTOM;
44028         this.split.orientation = Roo.SplitBar.VERTICAL;
44029         this.split.el.addClass("x-layout-split-v");
44030     }
44031     var size = config.initialSize || config.height;
44032     if(typeof size != "undefined"){
44033         this.el.setHeight(size);
44034     }
44035 };
44036 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44037     orientation: Roo.SplitBar.VERTICAL,
44038     getBox : function(){
44039         if(this.collapsed){
44040             return this.collapsedEl.getBox();
44041         }
44042         var box = this.el.getBox();
44043         if(this.split){
44044             var sh = this.split.el.getHeight();
44045             box.height += sh;
44046             box.y -= sh;
44047         }
44048         return box;
44049     },
44050     
44051     updateBox : function(box){
44052         if(this.split && !this.collapsed){
44053             var sh = this.split.el.getHeight();
44054             box.height -= sh;
44055             box.y += sh;
44056             this.split.el.setLeft(box.x);
44057             this.split.el.setTop(box.y-sh);
44058             this.split.el.setWidth(box.width);
44059         }
44060         if(this.collapsed){
44061             this.updateBody(box.width, null);
44062         }
44063         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44064     }
44065 });
44066
44067 Roo.EastLayoutRegion = function(mgr, config){
44068     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44069     if(this.split){
44070         this.split.placement = Roo.SplitBar.RIGHT;
44071         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44072         this.split.el.addClass("x-layout-split-h");
44073     }
44074     var size = config.initialSize || config.width;
44075     if(typeof size != "undefined"){
44076         this.el.setWidth(size);
44077     }
44078 };
44079 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44080     orientation: Roo.SplitBar.HORIZONTAL,
44081     getBox : function(){
44082         if(this.collapsed){
44083             return this.collapsedEl.getBox();
44084         }
44085         var box = this.el.getBox();
44086         if(this.split){
44087             var sw = this.split.el.getWidth();
44088             box.width += sw;
44089             box.x -= sw;
44090         }
44091         return box;
44092     },
44093
44094     updateBox : function(box){
44095         if(this.split && !this.collapsed){
44096             var sw = this.split.el.getWidth();
44097             box.width -= sw;
44098             this.split.el.setLeft(box.x);
44099             this.split.el.setTop(box.y);
44100             this.split.el.setHeight(box.height);
44101             box.x += sw;
44102         }
44103         if(this.collapsed){
44104             this.updateBody(null, box.height);
44105         }
44106         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44107     }
44108 });
44109
44110 Roo.WestLayoutRegion = function(mgr, config){
44111     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44112     if(this.split){
44113         this.split.placement = Roo.SplitBar.LEFT;
44114         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44115         this.split.el.addClass("x-layout-split-h");
44116     }
44117     var size = config.initialSize || config.width;
44118     if(typeof size != "undefined"){
44119         this.el.setWidth(size);
44120     }
44121 };
44122 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44123     orientation: Roo.SplitBar.HORIZONTAL,
44124     getBox : function(){
44125         if(this.collapsed){
44126             return this.collapsedEl.getBox();
44127         }
44128         var box = this.el.getBox();
44129         if(this.split){
44130             box.width += this.split.el.getWidth();
44131         }
44132         return box;
44133     },
44134     
44135     updateBox : function(box){
44136         if(this.split && !this.collapsed){
44137             var sw = this.split.el.getWidth();
44138             box.width -= sw;
44139             this.split.el.setLeft(box.x+box.width);
44140             this.split.el.setTop(box.y);
44141             this.split.el.setHeight(box.height);
44142         }
44143         if(this.collapsed){
44144             this.updateBody(null, box.height);
44145         }
44146         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44147     }
44148 });
44149 /*
44150  * Based on:
44151  * Ext JS Library 1.1.1
44152  * Copyright(c) 2006-2007, Ext JS, LLC.
44153  *
44154  * Originally Released Under LGPL - original licence link has changed is not relivant.
44155  *
44156  * Fork - LGPL
44157  * <script type="text/javascript">
44158  */
44159  
44160  
44161 /*
44162  * Private internal class for reading and applying state
44163  */
44164 Roo.LayoutStateManager = function(layout){
44165      // default empty state
44166      this.state = {
44167         north: {},
44168         south: {},
44169         east: {},
44170         west: {}       
44171     };
44172 };
44173
44174 Roo.LayoutStateManager.prototype = {
44175     init : function(layout, provider){
44176         this.provider = provider;
44177         var state = provider.get(layout.id+"-layout-state");
44178         if(state){
44179             var wasUpdating = layout.isUpdating();
44180             if(!wasUpdating){
44181                 layout.beginUpdate();
44182             }
44183             for(var key in state){
44184                 if(typeof state[key] != "function"){
44185                     var rstate = state[key];
44186                     var r = layout.getRegion(key);
44187                     if(r && rstate){
44188                         if(rstate.size){
44189                             r.resizeTo(rstate.size);
44190                         }
44191                         if(rstate.collapsed == true){
44192                             r.collapse(true);
44193                         }else{
44194                             r.expand(null, true);
44195                         }
44196                     }
44197                 }
44198             }
44199             if(!wasUpdating){
44200                 layout.endUpdate();
44201             }
44202             this.state = state; 
44203         }
44204         this.layout = layout;
44205         layout.on("regionresized", this.onRegionResized, this);
44206         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44207         layout.on("regionexpanded", this.onRegionExpanded, this);
44208     },
44209     
44210     storeState : function(){
44211         this.provider.set(this.layout.id+"-layout-state", this.state);
44212     },
44213     
44214     onRegionResized : function(region, newSize){
44215         this.state[region.getPosition()].size = newSize;
44216         this.storeState();
44217     },
44218     
44219     onRegionCollapsed : function(region){
44220         this.state[region.getPosition()].collapsed = true;
44221         this.storeState();
44222     },
44223     
44224     onRegionExpanded : function(region){
44225         this.state[region.getPosition()].collapsed = false;
44226         this.storeState();
44227     }
44228 };/*
44229  * Based on:
44230  * Ext JS Library 1.1.1
44231  * Copyright(c) 2006-2007, Ext JS, LLC.
44232  *
44233  * Originally Released Under LGPL - original licence link has changed is not relivant.
44234  *
44235  * Fork - LGPL
44236  * <script type="text/javascript">
44237  */
44238 /**
44239  * @class Roo.ContentPanel
44240  * @extends Roo.util.Observable
44241  * A basic ContentPanel element.
44242  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44243  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44244  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
44245  * @cfg {Boolean} closable True if the panel can be closed/removed
44246  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44247  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44248  * @cfg {Toolbar} toolbar A toolbar for this panel
44249  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44250  * @cfg {String} title The title for this panel
44251  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44252  * @cfg {String} url Calls {@link #setUrl} with this value
44253  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44254  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44255  * @constructor
44256  * Create a new ContentPanel.
44257  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44258  * @param {String/Object} config A string to set only the title or a config object
44259  * @param {String} content (optional) Set the HTML content for this panel
44260  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44261  */
44262 Roo.ContentPanel = function(el, config, content){
44263     
44264      
44265     /*
44266     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44267         config = el;
44268         el = Roo.id();
44269     }
44270     if (config && config.parentLayout) { 
44271         el = config.parentLayout.el.createChild(); 
44272     }
44273     */
44274     if(el.autoCreate){ // xtype is available if this is called from factory
44275         config = el;
44276         el = Roo.id();
44277     }
44278     this.el = Roo.get(el);
44279     if(!this.el && config && config.autoCreate){
44280         if(typeof config.autoCreate == "object"){
44281             if(!config.autoCreate.id){
44282                 config.autoCreate.id = config.id||el;
44283             }
44284             this.el = Roo.DomHelper.append(document.body,
44285                         config.autoCreate, true);
44286         }else{
44287             this.el = Roo.DomHelper.append(document.body,
44288                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44289         }
44290     }
44291     this.closable = false;
44292     this.loaded = false;
44293     this.active = false;
44294     if(typeof config == "string"){
44295         this.title = config;
44296     }else{
44297         Roo.apply(this, config);
44298     }
44299     
44300     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44301         this.wrapEl = this.el.wrap();    
44302         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44303         
44304     }
44305     
44306     
44307     
44308     if(this.resizeEl){
44309         this.resizeEl = Roo.get(this.resizeEl, true);
44310     }else{
44311         this.resizeEl = this.el;
44312     }
44313     this.addEvents({
44314         /**
44315          * @event activate
44316          * Fires when this panel is activated. 
44317          * @param {Roo.ContentPanel} this
44318          */
44319         "activate" : true,
44320         /**
44321          * @event deactivate
44322          * Fires when this panel is activated. 
44323          * @param {Roo.ContentPanel} this
44324          */
44325         "deactivate" : true,
44326
44327         /**
44328          * @event resize
44329          * Fires when this panel is resized if fitToFrame is true.
44330          * @param {Roo.ContentPanel} this
44331          * @param {Number} width The width after any component adjustments
44332          * @param {Number} height The height after any component adjustments
44333          */
44334         "resize" : true
44335     });
44336     if(this.autoScroll){
44337         this.resizeEl.setStyle("overflow", "auto");
44338     }
44339     content = content || this.content;
44340     if(content){
44341         this.setContent(content);
44342     }
44343     if(config && config.url){
44344         this.setUrl(this.url, this.params, this.loadOnce);
44345     }
44346     
44347     
44348     
44349     Roo.ContentPanel.superclass.constructor.call(this);
44350 };
44351
44352 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44353     tabTip:'',
44354     setRegion : function(region){
44355         this.region = region;
44356         if(region){
44357            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44358         }else{
44359            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44360         } 
44361     },
44362     
44363     /**
44364      * Returns the toolbar for this Panel if one was configured. 
44365      * @return {Roo.Toolbar} 
44366      */
44367     getToolbar : function(){
44368         return this.toolbar;
44369     },
44370     
44371     setActiveState : function(active){
44372         this.active = active;
44373         if(!active){
44374             this.fireEvent("deactivate", this);
44375         }else{
44376             this.fireEvent("activate", this);
44377         }
44378     },
44379     /**
44380      * Updates this panel's element
44381      * @param {String} content The new content
44382      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44383     */
44384     setContent : function(content, loadScripts){
44385         this.el.update(content, loadScripts);
44386     },
44387
44388     ignoreResize : function(w, h){
44389         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44390             return true;
44391         }else{
44392             this.lastSize = {width: w, height: h};
44393             return false;
44394         }
44395     },
44396     /**
44397      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44398      * @return {Roo.UpdateManager} The UpdateManager
44399      */
44400     getUpdateManager : function(){
44401         return this.el.getUpdateManager();
44402     },
44403      /**
44404      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44405      * @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:
44406 <pre><code>
44407 panel.load({
44408     url: "your-url.php",
44409     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44410     callback: yourFunction,
44411     scope: yourObject, //(optional scope)
44412     discardUrl: false,
44413     nocache: false,
44414     text: "Loading...",
44415     timeout: 30,
44416     scripts: false
44417 });
44418 </code></pre>
44419      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44420      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
44421      * @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}
44422      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44423      * @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.
44424      * @return {Roo.ContentPanel} this
44425      */
44426     load : function(){
44427         var um = this.el.getUpdateManager();
44428         um.update.apply(um, arguments);
44429         return this;
44430     },
44431
44432
44433     /**
44434      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
44435      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44436      * @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)
44437      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
44438      * @return {Roo.UpdateManager} The UpdateManager
44439      */
44440     setUrl : function(url, params, loadOnce){
44441         if(this.refreshDelegate){
44442             this.removeListener("activate", this.refreshDelegate);
44443         }
44444         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44445         this.on("activate", this.refreshDelegate);
44446         return this.el.getUpdateManager();
44447     },
44448     
44449     _handleRefresh : function(url, params, loadOnce){
44450         if(!loadOnce || !this.loaded){
44451             var updater = this.el.getUpdateManager();
44452             updater.update(url, params, this._setLoaded.createDelegate(this));
44453         }
44454     },
44455     
44456     _setLoaded : function(){
44457         this.loaded = true;
44458     }, 
44459     
44460     /**
44461      * Returns this panel's id
44462      * @return {String} 
44463      */
44464     getId : function(){
44465         return this.el.id;
44466     },
44467     
44468     /** 
44469      * Returns this panel's element - used by regiosn to add.
44470      * @return {Roo.Element} 
44471      */
44472     getEl : function(){
44473         return this.wrapEl || this.el;
44474     },
44475     
44476     adjustForComponents : function(width, height){
44477         if(this.resizeEl != this.el){
44478             width -= this.el.getFrameWidth('lr');
44479             height -= this.el.getFrameWidth('tb');
44480         }
44481         if(this.toolbar){
44482             var te = this.toolbar.getEl();
44483             height -= te.getHeight();
44484             te.setWidth(width);
44485         }
44486         if(this.adjustments){
44487             width += this.adjustments[0];
44488             height += this.adjustments[1];
44489         }
44490         return {"width": width, "height": height};
44491     },
44492     
44493     setSize : function(width, height){
44494         if(this.fitToFrame && !this.ignoreResize(width, height)){
44495             if(this.fitContainer && this.resizeEl != this.el){
44496                 this.el.setSize(width, height);
44497             }
44498             var size = this.adjustForComponents(width, height);
44499             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44500             this.fireEvent('resize', this, size.width, size.height);
44501         }
44502     },
44503     
44504     /**
44505      * Returns this panel's title
44506      * @return {String} 
44507      */
44508     getTitle : function(){
44509         return this.title;
44510     },
44511     
44512     /**
44513      * Set this panel's title
44514      * @param {String} title
44515      */
44516     setTitle : function(title){
44517         this.title = title;
44518         if(this.region){
44519             this.region.updatePanelTitle(this, title);
44520         }
44521     },
44522     
44523     /**
44524      * Returns true is this panel was configured to be closable
44525      * @return {Boolean} 
44526      */
44527     isClosable : function(){
44528         return this.closable;
44529     },
44530     
44531     beforeSlide : function(){
44532         this.el.clip();
44533         this.resizeEl.clip();
44534     },
44535     
44536     afterSlide : function(){
44537         this.el.unclip();
44538         this.resizeEl.unclip();
44539     },
44540     
44541     /**
44542      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44543      *   Will fail silently if the {@link #setUrl} method has not been called.
44544      *   This does not activate the panel, just updates its content.
44545      */
44546     refresh : function(){
44547         if(this.refreshDelegate){
44548            this.loaded = false;
44549            this.refreshDelegate();
44550         }
44551     },
44552     
44553     /**
44554      * Destroys this panel
44555      */
44556     destroy : function(){
44557         this.el.removeAllListeners();
44558         var tempEl = document.createElement("span");
44559         tempEl.appendChild(this.el.dom);
44560         tempEl.innerHTML = "";
44561         this.el.remove();
44562         this.el = null;
44563     },
44564     
44565       /**
44566      * Adds a xtype elements to the panel - currently only supports Forms.
44567      * <pre><code>
44568
44569 layout.addxtype({
44570        xtype : 'Form',
44571        items: [ .... ]
44572    }
44573 );
44574
44575 </code></pre>
44576      * @param {Object} cfg Xtype definition of item to add.
44577      */
44578     
44579     addxtype : function(cfg) {
44580         // add form..
44581         if (!cfg.xtype.match(/^Form$/)) {
44582             return false;
44583         }
44584         var el = this.el.createChild();
44585
44586         this.form = new  Roo.form.Form(cfg);
44587         
44588         
44589         if ( this.form.allItems.length) this.form.render(el.dom);
44590         return this.form;
44591         
44592     }
44593 });
44594
44595 /**
44596  * @class Roo.GridPanel
44597  * @extends Roo.ContentPanel
44598  * @constructor
44599  * Create a new GridPanel.
44600  * @param {Roo.grid.Grid} grid The grid for this panel
44601  * @param {String/Object} config A string to set only the panel's title, or a config object
44602  */
44603 Roo.GridPanel = function(grid, config){
44604     
44605   
44606     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44607         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44608         
44609     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44610     
44611     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44612     
44613     if(this.toolbar){
44614         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44615     }
44616     // xtype created footer. - not sure if will work as we normally have to render first..
44617     if (this.footer && !this.footer.el && this.footer.xtype) {
44618         
44619         this.footer.container = this.grid.getView().getFooterPanel(true);
44620         this.footer.dataSource = this.grid.dataSource;
44621         this.footer = Roo.factory(this.footer, Roo);
44622         
44623     }
44624     
44625     grid.monitorWindowResize = false; // turn off autosizing
44626     grid.autoHeight = false;
44627     grid.autoWidth = false;
44628     this.grid = grid;
44629     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44630 };
44631
44632 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44633     getId : function(){
44634         return this.grid.id;
44635     },
44636     
44637     /**
44638      * Returns the grid for this panel
44639      * @return {Roo.grid.Grid} 
44640      */
44641     getGrid : function(){
44642         return this.grid;    
44643     },
44644     
44645     setSize : function(width, height){
44646         if(!this.ignoreResize(width, height)){
44647             var grid = this.grid;
44648             var size = this.adjustForComponents(width, height);
44649             grid.getGridEl().setSize(size.width, size.height);
44650             grid.autoSize();
44651         }
44652     },
44653     
44654     beforeSlide : function(){
44655         this.grid.getView().scroller.clip();
44656     },
44657     
44658     afterSlide : function(){
44659         this.grid.getView().scroller.unclip();
44660     },
44661     
44662     destroy : function(){
44663         this.grid.destroy();
44664         delete this.grid;
44665         Roo.GridPanel.superclass.destroy.call(this); 
44666     }
44667 });
44668
44669
44670 /**
44671  * @class Roo.NestedLayoutPanel
44672  * @extends Roo.ContentPanel
44673  * @constructor
44674  * Create a new NestedLayoutPanel.
44675  * 
44676  * 
44677  * @param {Roo.BorderLayout} layout The layout for this panel
44678  * @param {String/Object} config A string to set only the title or a config object
44679  */
44680 Roo.NestedLayoutPanel = function(layout, config)
44681 {
44682     // construct with only one argument..
44683     /* FIXME - implement nicer consturctors
44684     if (layout.layout) {
44685         config = layout;
44686         layout = config.layout;
44687         delete config.layout;
44688     }
44689     if (layout.xtype && !layout.getEl) {
44690         // then layout needs constructing..
44691         layout = Roo.factory(layout, Roo);
44692     }
44693     */
44694     
44695     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44696     
44697     layout.monitorWindowResize = false; // turn off autosizing
44698     this.layout = layout;
44699     this.layout.getEl().addClass("x-layout-nested-layout");
44700     
44701     
44702     
44703 };
44704
44705 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44706
44707     setSize : function(width, height){
44708         if(!this.ignoreResize(width, height)){
44709             var size = this.adjustForComponents(width, height);
44710             var el = this.layout.getEl();
44711             el.setSize(size.width, size.height);
44712             var touch = el.dom.offsetWidth;
44713             this.layout.layout();
44714             // ie requires a double layout on the first pass
44715             if(Roo.isIE && !this.initialized){
44716                 this.initialized = true;
44717                 this.layout.layout();
44718             }
44719         }
44720     },
44721     
44722     // activate all subpanels if not currently active..
44723     
44724     setActiveState : function(active){
44725         this.active = active;
44726         if(!active){
44727             this.fireEvent("deactivate", this);
44728             return;
44729         }
44730         
44731         this.fireEvent("activate", this);
44732         // not sure if this should happen before or after..
44733         if (!this.layout) {
44734             return; // should not happen..
44735         }
44736         var reg = false;
44737         for (var r in this.layout.regions) {
44738             reg = this.layout.getRegion(r);
44739             if (reg.getActivePanel()) {
44740                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44741                 reg.setActivePanel(reg.getActivePanel());
44742                 continue;
44743             }
44744             if (!reg.panels.length) {
44745                 continue;
44746             }
44747             reg.showPanel(reg.getPanel(0));
44748         }
44749         
44750         
44751         
44752         
44753     },
44754     
44755     /**
44756      * Returns the nested BorderLayout for this panel
44757      * @return {Roo.BorderLayout} 
44758      */
44759     getLayout : function(){
44760         return this.layout;
44761     },
44762     
44763      /**
44764      * Adds a xtype elements to the layout of the nested panel
44765      * <pre><code>
44766
44767 panel.addxtype({
44768        xtype : 'ContentPanel',
44769        region: 'west',
44770        items: [ .... ]
44771    }
44772 );
44773
44774 panel.addxtype({
44775         xtype : 'NestedLayoutPanel',
44776         region: 'west',
44777         layout: {
44778            center: { },
44779            west: { }   
44780         },
44781         items : [ ... list of content panels or nested layout panels.. ]
44782    }
44783 );
44784 </code></pre>
44785      * @param {Object} cfg Xtype definition of item to add.
44786      */
44787     addxtype : function(cfg) {
44788         return this.layout.addxtype(cfg);
44789     
44790     }
44791 });
44792
44793 Roo.ScrollPanel = function(el, config, content){
44794     config = config || {};
44795     config.fitToFrame = true;
44796     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
44797     
44798     this.el.dom.style.overflow = "hidden";
44799     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
44800     this.el.removeClass("x-layout-inactive-content");
44801     this.el.on("mousewheel", this.onWheel, this);
44802
44803     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
44804     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
44805     up.unselectable(); down.unselectable();
44806     up.on("click", this.scrollUp, this);
44807     down.on("click", this.scrollDown, this);
44808     up.addClassOnOver("x-scroller-btn-over");
44809     down.addClassOnOver("x-scroller-btn-over");
44810     up.addClassOnClick("x-scroller-btn-click");
44811     down.addClassOnClick("x-scroller-btn-click");
44812     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
44813
44814     this.resizeEl = this.el;
44815     this.el = wrap; this.up = up; this.down = down;
44816 };
44817
44818 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
44819     increment : 100,
44820     wheelIncrement : 5,
44821     scrollUp : function(){
44822         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
44823     },
44824
44825     scrollDown : function(){
44826         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
44827     },
44828
44829     afterScroll : function(){
44830         var el = this.resizeEl;
44831         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
44832         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44833         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44834     },
44835
44836     setSize : function(){
44837         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
44838         this.afterScroll();
44839     },
44840
44841     onWheel : function(e){
44842         var d = e.getWheelDelta();
44843         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
44844         this.afterScroll();
44845         e.stopEvent();
44846     },
44847
44848     setContent : function(content, loadScripts){
44849         this.resizeEl.update(content, loadScripts);
44850     }
44851
44852 });
44853
44854
44855
44856
44857
44858
44859
44860
44861
44862 /**
44863  * @class Roo.TreePanel
44864  * @extends Roo.ContentPanel
44865  * @constructor
44866  * Create a new TreePanel.
44867  * @param {String/Object} config A string to set only the panel's title, or a config object
44868  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
44869  */
44870 Roo.TreePanel = function(config){
44871     var el = config.el;
44872     var tree = config.tree;
44873     delete config.tree; 
44874     delete config.el; // hopefull!
44875     Roo.TreePanel.superclass.constructor.call(this, el, config);
44876     var treeEl = el.createChild();
44877     this.tree = new Roo.tree.TreePanel(treeEl , tree);
44878     //console.log(tree);
44879     this.on('activate', function()
44880     {
44881         if (this.tree.rendered) {
44882             return;
44883         }
44884         //console.log('render tree');
44885         this.tree.render();
44886     });
44887     
44888     this.on('resize',  function (cp, w, h) {
44889             this.tree.innerCt.setWidth(w);
44890             this.tree.innerCt.setHeight(h);
44891             this.tree.innerCt.setStyle('overflow-y', 'auto');
44892     });
44893
44894         
44895     
44896 };
44897
44898 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
44899
44900
44901
44902
44903
44904
44905
44906
44907
44908
44909
44910 /*
44911  * Based on:
44912  * Ext JS Library 1.1.1
44913  * Copyright(c) 2006-2007, Ext JS, LLC.
44914  *
44915  * Originally Released Under LGPL - original licence link has changed is not relivant.
44916  *
44917  * Fork - LGPL
44918  * <script type="text/javascript">
44919  */
44920  
44921
44922 /**
44923  * @class Roo.ReaderLayout
44924  * @extends Roo.BorderLayout
44925  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
44926  * center region containing two nested regions (a top one for a list view and one for item preview below),
44927  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
44928  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
44929  * expedites the setup of the overall layout and regions for this common application style.
44930  * Example:
44931  <pre><code>
44932 var reader = new Roo.ReaderLayout();
44933 var CP = Roo.ContentPanel;  // shortcut for adding
44934
44935 reader.beginUpdate();
44936 reader.add("north", new CP("north", "North"));
44937 reader.add("west", new CP("west", {title: "West"}));
44938 reader.add("east", new CP("east", {title: "East"}));
44939
44940 reader.regions.listView.add(new CP("listView", "List"));
44941 reader.regions.preview.add(new CP("preview", "Preview"));
44942 reader.endUpdate();
44943 </code></pre>
44944 * @constructor
44945 * Create a new ReaderLayout
44946 * @param {Object} config Configuration options
44947 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
44948 * document.body if omitted)
44949 */
44950 Roo.ReaderLayout = function(config, renderTo){
44951     var c = config || {size:{}};
44952     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
44953         north: c.north !== false ? Roo.apply({
44954             split:false,
44955             initialSize: 32,
44956             titlebar: false
44957         }, c.north) : false,
44958         west: c.west !== false ? Roo.apply({
44959             split:true,
44960             initialSize: 200,
44961             minSize: 175,
44962             maxSize: 400,
44963             titlebar: true,
44964             collapsible: true,
44965             animate: true,
44966             margins:{left:5,right:0,bottom:5,top:5},
44967             cmargins:{left:5,right:5,bottom:5,top:5}
44968         }, c.west) : false,
44969         east: c.east !== false ? Roo.apply({
44970             split:true,
44971             initialSize: 200,
44972             minSize: 175,
44973             maxSize: 400,
44974             titlebar: true,
44975             collapsible: true,
44976             animate: true,
44977             margins:{left:0,right:5,bottom:5,top:5},
44978             cmargins:{left:5,right:5,bottom:5,top:5}
44979         }, c.east) : false,
44980         center: Roo.apply({
44981             tabPosition: 'top',
44982             autoScroll:false,
44983             closeOnTab: true,
44984             titlebar:false,
44985             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
44986         }, c.center)
44987     });
44988
44989     this.el.addClass('x-reader');
44990
44991     this.beginUpdate();
44992
44993     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
44994         south: c.preview !== false ? Roo.apply({
44995             split:true,
44996             initialSize: 200,
44997             minSize: 100,
44998             autoScroll:true,
44999             collapsible:true,
45000             titlebar: true,
45001             cmargins:{top:5,left:0, right:0, bottom:0}
45002         }, c.preview) : false,
45003         center: Roo.apply({
45004             autoScroll:false,
45005             titlebar:false,
45006             minHeight:200
45007         }, c.listView)
45008     });
45009     this.add('center', new Roo.NestedLayoutPanel(inner,
45010             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45011
45012     this.endUpdate();
45013
45014     this.regions.preview = inner.getRegion('south');
45015     this.regions.listView = inner.getRegion('center');
45016 };
45017
45018 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45019  * Based on:
45020  * Ext JS Library 1.1.1
45021  * Copyright(c) 2006-2007, Ext JS, LLC.
45022  *
45023  * Originally Released Under LGPL - original licence link has changed is not relivant.
45024  *
45025  * Fork - LGPL
45026  * <script type="text/javascript">
45027  */
45028  
45029 /**
45030  * @class Roo.grid.Grid
45031  * @extends Roo.util.Observable
45032  * This class represents the primary interface of a component based grid control.
45033  * <br><br>Usage:<pre><code>
45034  var grid = new Roo.grid.Grid("my-container-id", {
45035      ds: myDataStore,
45036      cm: myColModel,
45037      selModel: mySelectionModel,
45038      autoSizeColumns: true,
45039      monitorWindowResize: false,
45040      trackMouseOver: true
45041  });
45042  // set any options
45043  grid.render();
45044  * </code></pre>
45045  * <b>Common Problems:</b><br/>
45046  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45047  * element will correct this<br/>
45048  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45049  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45050  * are unpredictable.<br/>
45051  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45052  * grid to calculate dimensions/offsets.<br/>
45053   * @constructor
45054  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45055  * The container MUST have some type of size defined for the grid to fill. The container will be
45056  * automatically set to position relative if it isn't already.
45057  * @param {Object} config A config object that sets properties on this grid.
45058  */
45059 Roo.grid.Grid = function(container, config){
45060         // initialize the container
45061         this.container = Roo.get(container);
45062         this.container.update("");
45063         this.container.setStyle("overflow", "hidden");
45064     this.container.addClass('x-grid-container');
45065
45066     this.id = this.container.id;
45067
45068     Roo.apply(this, config);
45069     // check and correct shorthanded configs
45070     if(this.ds){
45071         this.dataSource = this.ds;
45072         delete this.ds;
45073     }
45074     if(this.cm){
45075         this.colModel = this.cm;
45076         delete this.cm;
45077     }
45078     if(this.sm){
45079         this.selModel = this.sm;
45080         delete this.sm;
45081     }
45082
45083     if (this.selModel) {
45084         this.selModel = Roo.factory(this.selModel, Roo.grid);
45085         this.sm = this.selModel;
45086         this.sm.xmodule = this.xmodule || false;
45087     }
45088     if (typeof(this.colModel.config) == 'undefined') {
45089         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45090         this.cm = this.colModel;
45091         this.cm.xmodule = this.xmodule || false;
45092     }
45093     if (this.dataSource) {
45094         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45095         this.ds = this.dataSource;
45096         this.ds.xmodule = this.xmodule || false;
45097         
45098     }
45099     
45100     
45101     
45102     if(this.width){
45103         this.container.setWidth(this.width);
45104     }
45105
45106     if(this.height){
45107         this.container.setHeight(this.height);
45108     }
45109     /** @private */
45110         this.addEvents({
45111             // raw events
45112             /**
45113              * @event click
45114              * The raw click event for the entire grid.
45115              * @param {Roo.EventObject} e
45116              */
45117             "click" : true,
45118             /**
45119              * @event dblclick
45120              * The raw dblclick event for the entire grid.
45121              * @param {Roo.EventObject} e
45122              */
45123             "dblclick" : true,
45124             /**
45125              * @event contextmenu
45126              * The raw contextmenu event for the entire grid.
45127              * @param {Roo.EventObject} e
45128              */
45129             "contextmenu" : true,
45130             /**
45131              * @event mousedown
45132              * The raw mousedown event for the entire grid.
45133              * @param {Roo.EventObject} e
45134              */
45135             "mousedown" : true,
45136             /**
45137              * @event mouseup
45138              * The raw mouseup event for the entire grid.
45139              * @param {Roo.EventObject} e
45140              */
45141             "mouseup" : true,
45142             /**
45143              * @event mouseover
45144              * The raw mouseover event for the entire grid.
45145              * @param {Roo.EventObject} e
45146              */
45147             "mouseover" : true,
45148             /**
45149              * @event mouseout
45150              * The raw mouseout event for the entire grid.
45151              * @param {Roo.EventObject} e
45152              */
45153             "mouseout" : true,
45154             /**
45155              * @event keypress
45156              * The raw keypress event for the entire grid.
45157              * @param {Roo.EventObject} e
45158              */
45159             "keypress" : true,
45160             /**
45161              * @event keydown
45162              * The raw keydown event for the entire grid.
45163              * @param {Roo.EventObject} e
45164              */
45165             "keydown" : true,
45166
45167             // custom events
45168
45169             /**
45170              * @event cellclick
45171              * Fires when a cell is clicked
45172              * @param {Grid} this
45173              * @param {Number} rowIndex
45174              * @param {Number} columnIndex
45175              * @param {Roo.EventObject} e
45176              */
45177             "cellclick" : true,
45178             /**
45179              * @event celldblclick
45180              * Fires when a cell is double clicked
45181              * @param {Grid} this
45182              * @param {Number} rowIndex
45183              * @param {Number} columnIndex
45184              * @param {Roo.EventObject} e
45185              */
45186             "celldblclick" : true,
45187             /**
45188              * @event rowclick
45189              * Fires when a row is clicked
45190              * @param {Grid} this
45191              * @param {Number} rowIndex
45192              * @param {Roo.EventObject} e
45193              */
45194             "rowclick" : true,
45195             /**
45196              * @event rowdblclick
45197              * Fires when a row is double clicked
45198              * @param {Grid} this
45199              * @param {Number} rowIndex
45200              * @param {Roo.EventObject} e
45201              */
45202             "rowdblclick" : true,
45203             /**
45204              * @event headerclick
45205              * Fires when a header is clicked
45206              * @param {Grid} this
45207              * @param {Number} columnIndex
45208              * @param {Roo.EventObject} e
45209              */
45210             "headerclick" : true,
45211             /**
45212              * @event headerdblclick
45213              * Fires when a header cell is double clicked
45214              * @param {Grid} this
45215              * @param {Number} columnIndex
45216              * @param {Roo.EventObject} e
45217              */
45218             "headerdblclick" : true,
45219             /**
45220              * @event rowcontextmenu
45221              * Fires when a row is right clicked
45222              * @param {Grid} this
45223              * @param {Number} rowIndex
45224              * @param {Roo.EventObject} e
45225              */
45226             "rowcontextmenu" : true,
45227             /**
45228          * @event cellcontextmenu
45229          * Fires when a cell is right clicked
45230          * @param {Grid} this
45231          * @param {Number} rowIndex
45232          * @param {Number} cellIndex
45233          * @param {Roo.EventObject} e
45234          */
45235          "cellcontextmenu" : true,
45236             /**
45237              * @event headercontextmenu
45238              * Fires when a header is right clicked
45239              * @param {Grid} this
45240              * @param {Number} columnIndex
45241              * @param {Roo.EventObject} e
45242              */
45243             "headercontextmenu" : true,
45244             /**
45245              * @event bodyscroll
45246              * Fires when the body element is scrolled
45247              * @param {Number} scrollLeft
45248              * @param {Number} scrollTop
45249              */
45250             "bodyscroll" : true,
45251             /**
45252              * @event columnresize
45253              * Fires when the user resizes a column
45254              * @param {Number} columnIndex
45255              * @param {Number} newSize
45256              */
45257             "columnresize" : true,
45258             /**
45259              * @event columnmove
45260              * Fires when the user moves a column
45261              * @param {Number} oldIndex
45262              * @param {Number} newIndex
45263              */
45264             "columnmove" : true,
45265             /**
45266              * @event startdrag
45267              * Fires when row(s) start being dragged
45268              * @param {Grid} this
45269              * @param {Roo.GridDD} dd The drag drop object
45270              * @param {event} e The raw browser event
45271              */
45272             "startdrag" : true,
45273             /**
45274              * @event enddrag
45275              * Fires when a drag operation is complete
45276              * @param {Grid} this
45277              * @param {Roo.GridDD} dd The drag drop object
45278              * @param {event} e The raw browser event
45279              */
45280             "enddrag" : true,
45281             /**
45282              * @event dragdrop
45283              * Fires when dragged row(s) are dropped on a valid DD target
45284              * @param {Grid} this
45285              * @param {Roo.GridDD} dd The drag drop object
45286              * @param {String} targetId The target drag drop object
45287              * @param {event} e The raw browser event
45288              */
45289             "dragdrop" : true,
45290             /**
45291              * @event dragover
45292              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45293              * @param {Grid} this
45294              * @param {Roo.GridDD} dd The drag drop object
45295              * @param {String} targetId The target drag drop object
45296              * @param {event} e The raw browser event
45297              */
45298             "dragover" : true,
45299             /**
45300              * @event dragenter
45301              *  Fires when the dragged row(s) first cross another DD target while being dragged
45302              * @param {Grid} this
45303              * @param {Roo.GridDD} dd The drag drop object
45304              * @param {String} targetId The target drag drop object
45305              * @param {event} e The raw browser event
45306              */
45307             "dragenter" : true,
45308             /**
45309              * @event dragout
45310              * Fires when the dragged row(s) leave another DD target while being dragged
45311              * @param {Grid} this
45312              * @param {Roo.GridDD} dd The drag drop object
45313              * @param {String} targetId The target drag drop object
45314              * @param {event} e The raw browser event
45315              */
45316             "dragout" : true,
45317         /**
45318          * @event render
45319          * Fires when the grid is rendered
45320          * @param {Grid} grid
45321          */
45322         render : true
45323     });
45324
45325     Roo.grid.Grid.superclass.constructor.call(this);
45326 };
45327 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45328     /**
45329      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45330          */
45331         minColumnWidth : 25,
45332
45333     /**
45334          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45335          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45336          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45337          */
45338         autoSizeColumns : false,
45339
45340         /**
45341          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45342          */
45343         autoSizeHeaders : true,
45344
45345         /**
45346          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45347          */
45348         monitorWindowResize : true,
45349
45350         /**
45351          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45352          * rows measured to get a columns size. Default is 0 (all rows).
45353          */
45354         maxRowsToMeasure : 0,
45355
45356         /**
45357          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45358          */
45359         trackMouseOver : true,
45360
45361         /**
45362          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45363          */
45364         enableDragDrop : false,
45365
45366         /**
45367          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45368          */
45369         enableColumnMove : true,
45370
45371         /**
45372          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45373          */
45374         enableColumnHide : true,
45375
45376         /**
45377          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45378          */
45379         enableRowHeightSync : false,
45380
45381         /**
45382          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45383          */
45384         stripeRows : true,
45385
45386         /**
45387          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45388          */
45389         autoHeight : false,
45390
45391     /**
45392      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
45393      */
45394     autoExpandColumn : false,
45395
45396     /**
45397     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45398     * Default is 50.
45399     */
45400     autoExpandMin : 50,
45401
45402     /**
45403     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45404     */
45405     autoExpandMax : 1000,
45406
45407     /**
45408          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45409          */
45410         view : null,
45411
45412         /**
45413      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45414          */
45415         loadMask : false,
45416
45417     // private
45418     rendered : false,
45419
45420     /**
45421     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45422     * of a fixed width. Default is false.
45423     */
45424     /**
45425     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45426     */
45427     /**
45428      * Called once after all setup has been completed and the grid is ready to be rendered.
45429      * @return {Roo.grid.Grid} this
45430      */
45431     render : function(){
45432         var c = this.container;
45433         // try to detect autoHeight/width mode
45434         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45435             this.autoHeight = true;
45436         }
45437         var view = this.getView();
45438         view.init(this);
45439
45440         c.on("click", this.onClick, this);
45441         c.on("dblclick", this.onDblClick, this);
45442         c.on("contextmenu", this.onContextMenu, this);
45443         c.on("keydown", this.onKeyDown, this);
45444
45445         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45446
45447         this.getSelectionModel().init(this);
45448
45449         view.render();
45450
45451         if(this.loadMask){
45452             this.loadMask = new Roo.LoadMask(this.container,
45453                     Roo.apply({store:this.dataSource}, this.loadMask));
45454         }
45455         
45456         
45457         if (this.toolbar && this.toolbar.xtype) {
45458             this.toolbar.container = this.getView().getHeaderPanel(true);
45459             this.toolbar = new Ext.Toolbar(this.toolbar);
45460         }
45461         if (this.footer && this.footer.xtype) {
45462             this.footer.dataSource = this.getDataSource();
45463             this.footer.container = this.getView().getFooterPanel(true);
45464             this.footer = Roo.factory(this.footer, Roo);
45465         }
45466         this.rendered = true;
45467         this.fireEvent('render', this);
45468         return this;
45469     },
45470
45471         /**
45472          * Reconfigures the grid to use a different Store and Column Model.
45473          * The View will be bound to the new objects and refreshed.
45474          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45475          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45476          */
45477     reconfigure : function(dataSource, colModel){
45478         if(this.loadMask){
45479             this.loadMask.destroy();
45480             this.loadMask = new Roo.LoadMask(this.container,
45481                     Roo.apply({store:dataSource}, this.loadMask));
45482         }
45483         this.view.bind(dataSource, colModel);
45484         this.dataSource = dataSource;
45485         this.colModel = colModel;
45486         this.view.refresh(true);
45487     },
45488
45489     // private
45490     onKeyDown : function(e){
45491         this.fireEvent("keydown", e);
45492     },
45493
45494     /**
45495      * Destroy this grid.
45496      * @param {Boolean} removeEl True to remove the element
45497      */
45498     destroy : function(removeEl, keepListeners){
45499         if(this.loadMask){
45500             this.loadMask.destroy();
45501         }
45502         var c = this.container;
45503         c.removeAllListeners();
45504         this.view.destroy();
45505         this.colModel.purgeListeners();
45506         if(!keepListeners){
45507             this.purgeListeners();
45508         }
45509         c.update("");
45510         if(removeEl === true){
45511             c.remove();
45512         }
45513     },
45514
45515     // private
45516     processEvent : function(name, e){
45517         this.fireEvent(name, e);
45518         var t = e.getTarget();
45519         var v = this.view;
45520         var header = v.findHeaderIndex(t);
45521         if(header !== false){
45522             this.fireEvent("header" + name, this, header, e);
45523         }else{
45524             var row = v.findRowIndex(t);
45525             var cell = v.findCellIndex(t);
45526             if(row !== false){
45527                 this.fireEvent("row" + name, this, row, e);
45528                 if(cell !== false){
45529                     this.fireEvent("cell" + name, this, row, cell, e);
45530                 }
45531             }
45532         }
45533     },
45534
45535     // private
45536     onClick : function(e){
45537         this.processEvent("click", e);
45538     },
45539
45540     // private
45541     onContextMenu : function(e, t){
45542         this.processEvent("contextmenu", e);
45543     },
45544
45545     // private
45546     onDblClick : function(e){
45547         this.processEvent("dblclick", e);
45548     },
45549
45550     // private
45551     walkCells : function(row, col, step, fn, scope){
45552         var cm = this.colModel, clen = cm.getColumnCount();
45553         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45554         if(step < 0){
45555             if(col < 0){
45556                 row--;
45557                 first = false;
45558             }
45559             while(row >= 0){
45560                 if(!first){
45561                     col = clen-1;
45562                 }
45563                 first = false;
45564                 while(col >= 0){
45565                     if(fn.call(scope || this, row, col, cm) === true){
45566                         return [row, col];
45567                     }
45568                     col--;
45569                 }
45570                 row--;
45571             }
45572         } else {
45573             if(col >= clen){
45574                 row++;
45575                 first = false;
45576             }
45577             while(row < rlen){
45578                 if(!first){
45579                     col = 0;
45580                 }
45581                 first = false;
45582                 while(col < clen){
45583                     if(fn.call(scope || this, row, col, cm) === true){
45584                         return [row, col];
45585                     }
45586                     col++;
45587                 }
45588                 row++;
45589             }
45590         }
45591         return null;
45592     },
45593
45594     // private
45595     getSelections : function(){
45596         return this.selModel.getSelections();
45597     },
45598
45599     /**
45600      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45601      * but if manual update is required this method will initiate it.
45602      */
45603     autoSize : function(){
45604         if(this.rendered){
45605             this.view.layout();
45606             if(this.view.adjustForScroll){
45607                 this.view.adjustForScroll();
45608             }
45609         }
45610     },
45611
45612     /**
45613      * Returns the grid's underlying element.
45614      * @return {Element} The element
45615      */
45616     getGridEl : function(){
45617         return this.container;
45618     },
45619
45620     // private for compatibility, overridden by editor grid
45621     stopEditing : function(){},
45622
45623     /**
45624      * Returns the grid's SelectionModel.
45625      * @return {SelectionModel}
45626      */
45627     getSelectionModel : function(){
45628         if(!this.selModel){
45629             this.selModel = new Roo.grid.RowSelectionModel();
45630         }
45631         return this.selModel;
45632     },
45633
45634     /**
45635      * Returns the grid's DataSource.
45636      * @return {DataSource}
45637      */
45638     getDataSource : function(){
45639         return this.dataSource;
45640     },
45641
45642     /**
45643      * Returns the grid's ColumnModel.
45644      * @return {ColumnModel}
45645      */
45646     getColumnModel : function(){
45647         return this.colModel;
45648     },
45649
45650     /**
45651      * Returns the grid's GridView object.
45652      * @return {GridView}
45653      */
45654     getView : function(){
45655         if(!this.view){
45656             this.view = new Roo.grid.GridView(this.viewConfig);
45657         }
45658         return this.view;
45659     },
45660     /**
45661      * Called to get grid's drag proxy text, by default returns this.ddText.
45662      * @return {String}
45663      */
45664     getDragDropText : function(){
45665         var count = this.selModel.getCount();
45666         return String.format(this.ddText, count, count == 1 ? '' : 's');
45667     }
45668 });
45669 /**
45670  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45671  * %0 is replaced with the number of selected rows.
45672  * @type String
45673  */
45674 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45675  * Based on:
45676  * Ext JS Library 1.1.1
45677  * Copyright(c) 2006-2007, Ext JS, LLC.
45678  *
45679  * Originally Released Under LGPL - original licence link has changed is not relivant.
45680  *
45681  * Fork - LGPL
45682  * <script type="text/javascript">
45683  */
45684  
45685 Roo.grid.AbstractGridView = function(){
45686         this.grid = null;
45687         
45688         this.events = {
45689             "beforerowremoved" : true,
45690             "beforerowsinserted" : true,
45691             "beforerefresh" : true,
45692             "rowremoved" : true,
45693             "rowsinserted" : true,
45694             "rowupdated" : true,
45695             "refresh" : true
45696         };
45697     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45698 };
45699
45700 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45701     rowClass : "x-grid-row",
45702     cellClass : "x-grid-cell",
45703     tdClass : "x-grid-td",
45704     hdClass : "x-grid-hd",
45705     splitClass : "x-grid-hd-split",
45706     
45707         init: function(grid){
45708         this.grid = grid;
45709                 var cid = this.grid.getGridEl().id;
45710         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45711         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45712         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45713         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45714         },
45715         
45716         getColumnRenderers : function(){
45717         var renderers = [];
45718         var cm = this.grid.colModel;
45719         var colCount = cm.getColumnCount();
45720         for(var i = 0; i < colCount; i++){
45721             renderers[i] = cm.getRenderer(i);
45722         }
45723         return renderers;
45724     },
45725     
45726     getColumnIds : function(){
45727         var ids = [];
45728         var cm = this.grid.colModel;
45729         var colCount = cm.getColumnCount();
45730         for(var i = 0; i < colCount; i++){
45731             ids[i] = cm.getColumnId(i);
45732         }
45733         return ids;
45734     },
45735     
45736     getDataIndexes : function(){
45737         if(!this.indexMap){
45738             this.indexMap = this.buildIndexMap();
45739         }
45740         return this.indexMap.colToData;
45741     },
45742     
45743     getColumnIndexByDataIndex : function(dataIndex){
45744         if(!this.indexMap){
45745             this.indexMap = this.buildIndexMap();
45746         }
45747         return this.indexMap.dataToCol[dataIndex];
45748     },
45749     
45750     /**
45751      * Set a css style for a column dynamically. 
45752      * @param {Number} colIndex The index of the column
45753      * @param {String} name The css property name
45754      * @param {String} value The css value
45755      */
45756     setCSSStyle : function(colIndex, name, value){
45757         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
45758         Roo.util.CSS.updateRule(selector, name, value);
45759     },
45760     
45761     generateRules : function(cm){
45762         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
45763         Roo.util.CSS.removeStyleSheet(rulesId);
45764         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
45765             var cid = cm.getColumnId(i);
45766             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
45767                          this.tdSelector, cid, " {\n}\n",
45768                          this.hdSelector, cid, " {\n}\n",
45769                          this.splitSelector, cid, " {\n}\n");
45770         }
45771         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
45772     }
45773 });/*
45774  * Based on:
45775  * Ext JS Library 1.1.1
45776  * Copyright(c) 2006-2007, Ext JS, LLC.
45777  *
45778  * Originally Released Under LGPL - original licence link has changed is not relivant.
45779  *
45780  * Fork - LGPL
45781  * <script type="text/javascript">
45782  */
45783
45784 // private
45785 // This is a support class used internally by the Grid components
45786 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
45787     this.grid = grid;
45788     this.view = grid.getView();
45789     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45790     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
45791     if(hd2){
45792         this.setHandleElId(Roo.id(hd));
45793         this.setOuterHandleElId(Roo.id(hd2));
45794     }
45795     this.scroll = false;
45796 };
45797 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
45798     maxDragWidth: 120,
45799     getDragData : function(e){
45800         var t = Roo.lib.Event.getTarget(e);
45801         var h = this.view.findHeaderCell(t);
45802         if(h){
45803             return {ddel: h.firstChild, header:h};
45804         }
45805         return false;
45806     },
45807
45808     onInitDrag : function(e){
45809         this.view.headersDisabled = true;
45810         var clone = this.dragData.ddel.cloneNode(true);
45811         clone.id = Roo.id();
45812         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
45813         this.proxy.update(clone);
45814         return true;
45815     },
45816
45817     afterValidDrop : function(){
45818         var v = this.view;
45819         setTimeout(function(){
45820             v.headersDisabled = false;
45821         }, 50);
45822     },
45823
45824     afterInvalidDrop : function(){
45825         var v = this.view;
45826         setTimeout(function(){
45827             v.headersDisabled = false;
45828         }, 50);
45829     }
45830 });
45831 /*
45832  * Based on:
45833  * Ext JS Library 1.1.1
45834  * Copyright(c) 2006-2007, Ext JS, LLC.
45835  *
45836  * Originally Released Under LGPL - original licence link has changed is not relivant.
45837  *
45838  * Fork - LGPL
45839  * <script type="text/javascript">
45840  */
45841 // private
45842 // This is a support class used internally by the Grid components
45843 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
45844     this.grid = grid;
45845     this.view = grid.getView();
45846     // split the proxies so they don't interfere with mouse events
45847     this.proxyTop = Roo.DomHelper.append(document.body, {
45848         cls:"col-move-top", html:"&#160;"
45849     }, true);
45850     this.proxyBottom = Roo.DomHelper.append(document.body, {
45851         cls:"col-move-bottom", html:"&#160;"
45852     }, true);
45853     this.proxyTop.hide = this.proxyBottom.hide = function(){
45854         this.setLeftTop(-100,-100);
45855         this.setStyle("visibility", "hidden");
45856     };
45857     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45858     // temporarily disabled
45859     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
45860     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
45861 };
45862 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
45863     proxyOffsets : [-4, -9],
45864     fly: Roo.Element.fly,
45865
45866     getTargetFromEvent : function(e){
45867         var t = Roo.lib.Event.getTarget(e);
45868         var cindex = this.view.findCellIndex(t);
45869         if(cindex !== false){
45870             return this.view.getHeaderCell(cindex);
45871         }
45872     },
45873
45874     nextVisible : function(h){
45875         var v = this.view, cm = this.grid.colModel;
45876         h = h.nextSibling;
45877         while(h){
45878             if(!cm.isHidden(v.getCellIndex(h))){
45879                 return h;
45880             }
45881             h = h.nextSibling;
45882         }
45883         return null;
45884     },
45885
45886     prevVisible : function(h){
45887         var v = this.view, cm = this.grid.colModel;
45888         h = h.prevSibling;
45889         while(h){
45890             if(!cm.isHidden(v.getCellIndex(h))){
45891                 return h;
45892             }
45893             h = h.prevSibling;
45894         }
45895         return null;
45896     },
45897
45898     positionIndicator : function(h, n, e){
45899         var x = Roo.lib.Event.getPageX(e);
45900         var r = Roo.lib.Dom.getRegion(n.firstChild);
45901         var px, pt, py = r.top + this.proxyOffsets[1];
45902         if((r.right - x) <= (r.right-r.left)/2){
45903             px = r.right+this.view.borderWidth;
45904             pt = "after";
45905         }else{
45906             px = r.left;
45907             pt = "before";
45908         }
45909         var oldIndex = this.view.getCellIndex(h);
45910         var newIndex = this.view.getCellIndex(n);
45911
45912         if(this.grid.colModel.isFixed(newIndex)){
45913             return false;
45914         }
45915
45916         var locked = this.grid.colModel.isLocked(newIndex);
45917
45918         if(pt == "after"){
45919             newIndex++;
45920         }
45921         if(oldIndex < newIndex){
45922             newIndex--;
45923         }
45924         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
45925             return false;
45926         }
45927         px +=  this.proxyOffsets[0];
45928         this.proxyTop.setLeftTop(px, py);
45929         this.proxyTop.show();
45930         if(!this.bottomOffset){
45931             this.bottomOffset = this.view.mainHd.getHeight();
45932         }
45933         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
45934         this.proxyBottom.show();
45935         return pt;
45936     },
45937
45938     onNodeEnter : function(n, dd, e, data){
45939         if(data.header != n){
45940             this.positionIndicator(data.header, n, e);
45941         }
45942     },
45943
45944     onNodeOver : function(n, dd, e, data){
45945         var result = false;
45946         if(data.header != n){
45947             result = this.positionIndicator(data.header, n, e);
45948         }
45949         if(!result){
45950             this.proxyTop.hide();
45951             this.proxyBottom.hide();
45952         }
45953         return result ? this.dropAllowed : this.dropNotAllowed;
45954     },
45955
45956     onNodeOut : function(n, dd, e, data){
45957         this.proxyTop.hide();
45958         this.proxyBottom.hide();
45959     },
45960
45961     onNodeDrop : function(n, dd, e, data){
45962         var h = data.header;
45963         if(h != n){
45964             var cm = this.grid.colModel;
45965             var x = Roo.lib.Event.getPageX(e);
45966             var r = Roo.lib.Dom.getRegion(n.firstChild);
45967             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
45968             var oldIndex = this.view.getCellIndex(h);
45969             var newIndex = this.view.getCellIndex(n);
45970             var locked = cm.isLocked(newIndex);
45971             if(pt == "after"){
45972                 newIndex++;
45973             }
45974             if(oldIndex < newIndex){
45975                 newIndex--;
45976             }
45977             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
45978                 return false;
45979             }
45980             cm.setLocked(oldIndex, locked, true);
45981             cm.moveColumn(oldIndex, newIndex);
45982             this.grid.fireEvent("columnmove", oldIndex, newIndex);
45983             return true;
45984         }
45985         return false;
45986     }
45987 });
45988 /*
45989  * Based on:
45990  * Ext JS Library 1.1.1
45991  * Copyright(c) 2006-2007, Ext JS, LLC.
45992  *
45993  * Originally Released Under LGPL - original licence link has changed is not relivant.
45994  *
45995  * Fork - LGPL
45996  * <script type="text/javascript">
45997  */
45998   
45999 /**
46000  * @class Roo.grid.GridView
46001  * @extends Roo.util.Observable
46002  *
46003  * @constructor
46004  * @param {Object} config
46005  */
46006 Roo.grid.GridView = function(config){
46007     Roo.grid.GridView.superclass.constructor.call(this);
46008     this.el = null;
46009
46010     Roo.apply(this, config);
46011 };
46012
46013 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46014
46015     /**
46016      * Override this function to apply custom css classes to rows during rendering
46017      * @param {Record} record The record
46018      * @param {Number} index
46019      * @method getRowClass
46020      */
46021     rowClass : "x-grid-row",
46022
46023     cellClass : "x-grid-col",
46024
46025     tdClass : "x-grid-td",
46026
46027     hdClass : "x-grid-hd",
46028
46029     splitClass : "x-grid-split",
46030
46031     sortClasses : ["sort-asc", "sort-desc"],
46032
46033     enableMoveAnim : false,
46034
46035     hlColor: "C3DAF9",
46036
46037     dh : Roo.DomHelper,
46038
46039     fly : Roo.Element.fly,
46040
46041     css : Roo.util.CSS,
46042
46043     borderWidth: 1,
46044
46045     splitOffset: 3,
46046
46047     scrollIncrement : 22,
46048
46049     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46050
46051     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46052
46053     bind : function(ds, cm){
46054         if(this.ds){
46055             this.ds.un("load", this.onLoad, this);
46056             this.ds.un("datachanged", this.onDataChange, this);
46057             this.ds.un("add", this.onAdd, this);
46058             this.ds.un("remove", this.onRemove, this);
46059             this.ds.un("update", this.onUpdate, this);
46060             this.ds.un("clear", this.onClear, this);
46061         }
46062         if(ds){
46063             ds.on("load", this.onLoad, this);
46064             ds.on("datachanged", this.onDataChange, this);
46065             ds.on("add", this.onAdd, this);
46066             ds.on("remove", this.onRemove, this);
46067             ds.on("update", this.onUpdate, this);
46068             ds.on("clear", this.onClear, this);
46069         }
46070         this.ds = ds;
46071
46072         if(this.cm){
46073             this.cm.un("widthchange", this.onColWidthChange, this);
46074             this.cm.un("headerchange", this.onHeaderChange, this);
46075             this.cm.un("hiddenchange", this.onHiddenChange, this);
46076             this.cm.un("columnmoved", this.onColumnMove, this);
46077             this.cm.un("columnlockchange", this.onColumnLock, this);
46078         }
46079         if(cm){
46080             this.generateRules(cm);
46081             cm.on("widthchange", this.onColWidthChange, this);
46082             cm.on("headerchange", this.onHeaderChange, this);
46083             cm.on("hiddenchange", this.onHiddenChange, this);
46084             cm.on("columnmoved", this.onColumnMove, this);
46085             cm.on("columnlockchange", this.onColumnLock, this);
46086         }
46087         this.cm = cm;
46088     },
46089
46090     init: function(grid){
46091                 Roo.grid.GridView.superclass.init.call(this, grid);
46092
46093                 this.bind(grid.dataSource, grid.colModel);
46094
46095             grid.on("headerclick", this.handleHeaderClick, this);
46096
46097         if(grid.trackMouseOver){
46098             grid.on("mouseover", this.onRowOver, this);
46099                 grid.on("mouseout", this.onRowOut, this);
46100             }
46101             grid.cancelTextSelection = function(){};
46102                 this.gridId = grid.id;
46103
46104                 var tpls = this.templates || {};
46105
46106                 if(!tpls.master){
46107                     tpls.master = new Roo.Template(
46108                        '<div class="x-grid" hidefocus="true">',
46109                           '<div class="x-grid-topbar"></div>',
46110                           '<div class="x-grid-scroller"><div></div></div>',
46111                           '<div class="x-grid-locked">',
46112                               '<div class="x-grid-header">{lockedHeader}</div>',
46113                               '<div class="x-grid-body">{lockedBody}</div>',
46114                           "</div>",
46115                           '<div class="x-grid-viewport">',
46116                               '<div class="x-grid-header">{header}</div>',
46117                               '<div class="x-grid-body">{body}</div>',
46118                           "</div>",
46119                           '<div class="x-grid-bottombar"></div>',
46120                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46121                           '<div class="x-grid-resize-proxy">&#160;</div>',
46122                        "</div>"
46123                     );
46124                     tpls.master.disableformats = true;
46125                 }
46126
46127                 if(!tpls.header){
46128                     tpls.header = new Roo.Template(
46129                        '<table border="0" cellspacing="0" cellpadding="0">',
46130                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46131                        "</table>{splits}"
46132                     );
46133                     tpls.header.disableformats = true;
46134                 }
46135                 tpls.header.compile();
46136
46137                 if(!tpls.hcell){
46138                     tpls.hcell = new Roo.Template(
46139                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46140                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46141                         "</div></td>"
46142                      );
46143                      tpls.hcell.disableFormats = true;
46144                 }
46145                 tpls.hcell.compile();
46146
46147                 if(!tpls.hsplit){
46148                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46149                     tpls.hsplit.disableFormats = true;
46150                 }
46151                 tpls.hsplit.compile();
46152
46153                 if(!tpls.body){
46154                     tpls.body = new Roo.Template(
46155                        '<table border="0" cellspacing="0" cellpadding="0">',
46156                        "<tbody>{rows}</tbody>",
46157                        "</table>"
46158                     );
46159                     tpls.body.disableFormats = true;
46160                 }
46161                 tpls.body.compile();
46162
46163                 if(!tpls.row){
46164                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46165                     tpls.row.disableFormats = true;
46166                 }
46167                 tpls.row.compile();
46168
46169                 if(!tpls.cell){
46170                     tpls.cell = new Roo.Template(
46171                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46172                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46173                         "</td>"
46174                     );
46175             tpls.cell.disableFormats = true;
46176         }
46177                 tpls.cell.compile();
46178
46179                 this.templates = tpls;
46180         },
46181
46182         // remap these for backwards compat
46183     onColWidthChange : function(){
46184         this.updateColumns.apply(this, arguments);
46185     },
46186     onHeaderChange : function(){
46187         this.updateHeaders.apply(this, arguments);
46188     }, 
46189     onHiddenChange : function(){
46190         this.handleHiddenChange.apply(this, arguments);
46191     },
46192     onColumnMove : function(){
46193         this.handleColumnMove.apply(this, arguments);
46194     },
46195     onColumnLock : function(){
46196         this.handleLockChange.apply(this, arguments);
46197     },
46198
46199     onDataChange : function(){
46200         this.refresh();
46201         this.updateHeaderSortState();
46202     },
46203
46204         onClear : function(){
46205         this.refresh();
46206     },
46207
46208         onUpdate : function(ds, record){
46209         this.refreshRow(record);
46210     },
46211
46212     refreshRow : function(record){
46213         var ds = this.ds, index;
46214         if(typeof record == 'number'){
46215             index = record;
46216             record = ds.getAt(index);
46217         }else{
46218             index = ds.indexOf(record);
46219         }
46220         this.insertRows(ds, index, index, true);
46221         this.onRemove(ds, record, index+1, true);
46222         this.syncRowHeights(index, index);
46223         this.layout();
46224         this.fireEvent("rowupdated", this, index, record);
46225     },
46226
46227     onAdd : function(ds, records, index){
46228         this.insertRows(ds, index, index + (records.length-1));
46229     },
46230
46231     onRemove : function(ds, record, index, isUpdate){
46232         if(isUpdate !== true){
46233             this.fireEvent("beforerowremoved", this, index, record);
46234         }
46235         var bt = this.getBodyTable(), lt = this.getLockedTable();
46236         if(bt.rows[index]){
46237             bt.firstChild.removeChild(bt.rows[index]);
46238         }
46239         if(lt.rows[index]){
46240             lt.firstChild.removeChild(lt.rows[index]);
46241         }
46242         if(isUpdate !== true){
46243             this.stripeRows(index);
46244             this.syncRowHeights(index, index);
46245             this.layout();
46246             this.fireEvent("rowremoved", this, index, record);
46247         }
46248     },
46249
46250     onLoad : function(){
46251         this.scrollToTop();
46252     },
46253
46254     /**
46255      * Scrolls the grid to the top
46256      */
46257     scrollToTop : function(){
46258         if(this.scroller){
46259             this.scroller.dom.scrollTop = 0;
46260             this.syncScroll();
46261         }
46262     },
46263
46264     /**
46265      * Gets a panel in the header of the grid that can be used for toolbars etc.
46266      * After modifying the contents of this panel a call to grid.autoSize() may be
46267      * required to register any changes in size.
46268      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46269      * @return Roo.Element
46270      */
46271     getHeaderPanel : function(doShow){
46272         if(doShow){
46273             this.headerPanel.show();
46274         }
46275         return this.headerPanel;
46276         },
46277
46278         /**
46279      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46280      * After modifying the contents of this panel a call to grid.autoSize() may be
46281      * required to register any changes in size.
46282      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46283      * @return Roo.Element
46284      */
46285     getFooterPanel : function(doShow){
46286         if(doShow){
46287             this.footerPanel.show();
46288         }
46289         return this.footerPanel;
46290         },
46291
46292         initElements : function(){
46293             var E = Roo.Element;
46294             var el = this.grid.getGridEl().dom.firstChild;
46295             var cs = el.childNodes;
46296
46297             this.el = new E(el);
46298             this.headerPanel = new E(el.firstChild);
46299             this.headerPanel.enableDisplayMode("block");
46300
46301         this.scroller = new E(cs[1]);
46302             this.scrollSizer = new E(this.scroller.dom.firstChild);
46303
46304             this.lockedWrap = new E(cs[2]);
46305             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46306             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46307
46308             this.mainWrap = new E(cs[3]);
46309             this.mainHd = new E(this.mainWrap.dom.firstChild);
46310             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46311
46312             this.footerPanel = new E(cs[4]);
46313             this.footerPanel.enableDisplayMode("block");
46314
46315         this.focusEl = new E(cs[5]);
46316         this.focusEl.swallowEvent("click", true);
46317         this.resizeProxy = new E(cs[6]);
46318
46319             this.headerSelector = String.format(
46320                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46321                this.lockedHd.id, this.mainHd.id
46322             );
46323
46324             this.splitterSelector = String.format(
46325                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46326                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46327             );
46328     },
46329     idToCssName : function(s)
46330     {
46331         return s.replace(/[^a-z0-9]+/ig, '-');
46332     },
46333
46334         getHeaderCell : function(index){
46335             return Roo.DomQuery.select(this.headerSelector)[index];
46336         },
46337
46338         getHeaderCellMeasure : function(index){
46339             return this.getHeaderCell(index).firstChild;
46340         },
46341
46342         getHeaderCellText : function(index){
46343             return this.getHeaderCell(index).firstChild.firstChild;
46344         },
46345
46346         getLockedTable : function(){
46347             return this.lockedBody.dom.firstChild;
46348         },
46349
46350         getBodyTable : function(){
46351             return this.mainBody.dom.firstChild;
46352         },
46353
46354         getLockedRow : function(index){
46355             return this.getLockedTable().rows[index];
46356         },
46357
46358         getRow : function(index){
46359             return this.getBodyTable().rows[index];
46360         },
46361
46362         getRowComposite : function(index){
46363             if(!this.rowEl){
46364                 this.rowEl = new Roo.CompositeElementLite();
46365             }
46366         var els = [], lrow, mrow;
46367         if(lrow = this.getLockedRow(index)){
46368             els.push(lrow);
46369         }
46370         if(mrow = this.getRow(index)){
46371             els.push(mrow);
46372         }
46373         this.rowEl.elements = els;
46374             return this.rowEl;
46375         },
46376
46377         getCell : function(rowIndex, colIndex){
46378             var locked = this.cm.getLockedCount();
46379             var source;
46380             if(colIndex < locked){
46381                 source = this.lockedBody.dom.firstChild;
46382             }else{
46383                 source = this.mainBody.dom.firstChild;
46384                 colIndex -= locked;
46385             }
46386         return source.rows[rowIndex].childNodes[colIndex];
46387         },
46388
46389         getCellText : function(rowIndex, colIndex){
46390             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46391         },
46392
46393         getCellBox : function(cell){
46394             var b = this.fly(cell).getBox();
46395         if(Roo.isOpera){ // opera fails to report the Y
46396             b.y = cell.offsetTop + this.mainBody.getY();
46397         }
46398         return b;
46399     },
46400
46401     getCellIndex : function(cell){
46402         var id = String(cell.className).match(this.cellRE);
46403         if(id){
46404             return parseInt(id[1], 10);
46405         }
46406         return 0;
46407     },
46408
46409     findHeaderIndex : function(n){
46410         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46411         return r ? this.getCellIndex(r) : false;
46412     },
46413
46414     findHeaderCell : function(n){
46415         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46416         return r ? r : false;
46417     },
46418
46419     findRowIndex : function(n){
46420         if(!n){
46421             return false;
46422         }
46423         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46424         return r ? r.rowIndex : false;
46425     },
46426
46427     findCellIndex : function(node){
46428         var stop = this.el.dom;
46429         while(node && node != stop){
46430             if(this.findRE.test(node.className)){
46431                 return this.getCellIndex(node);
46432             }
46433             node = node.parentNode;
46434         }
46435         return false;
46436     },
46437
46438     getColumnId : function(index){
46439             return this.cm.getColumnId(index);
46440         },
46441
46442         getSplitters : function(){
46443             if(this.splitterSelector){
46444                return Roo.DomQuery.select(this.splitterSelector);
46445             }else{
46446                 return null;
46447             }
46448         },
46449
46450         getSplitter : function(index){
46451             return this.getSplitters()[index];
46452         },
46453
46454     onRowOver : function(e, t){
46455         var row;
46456         if((row = this.findRowIndex(t)) !== false){
46457             this.getRowComposite(row).addClass("x-grid-row-over");
46458         }
46459     },
46460
46461     onRowOut : function(e, t){
46462         var row;
46463         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46464             this.getRowComposite(row).removeClass("x-grid-row-over");
46465         }
46466     },
46467
46468     renderHeaders : function(){
46469             var cm = this.cm;
46470         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46471         var cb = [], lb = [], sb = [], lsb = [], p = {};
46472         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46473             p.cellId = "x-grid-hd-0-" + i;
46474             p.splitId = "x-grid-csplit-0-" + i;
46475             p.id = cm.getColumnId(i);
46476             p.title = cm.getColumnTooltip(i) || "";
46477             p.value = cm.getColumnHeader(i) || "";
46478             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46479             if(!cm.isLocked(i)){
46480                 cb[cb.length] = ct.apply(p);
46481                 sb[sb.length] = st.apply(p);
46482             }else{
46483                 lb[lb.length] = ct.apply(p);
46484                 lsb[lsb.length] = st.apply(p);
46485             }
46486         }
46487         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46488                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46489         },
46490
46491         updateHeaders : function(){
46492         var html = this.renderHeaders();
46493         this.lockedHd.update(html[0]);
46494         this.mainHd.update(html[1]);
46495     },
46496
46497     /**
46498      * Focuses the specified row.
46499      * @param {Number} row The row index
46500      */
46501     focusRow : function(row){
46502         var x = this.scroller.dom.scrollLeft;
46503         this.focusCell(row, 0, false);
46504         this.scroller.dom.scrollLeft = x;
46505     },
46506
46507     /**
46508      * Focuses the specified cell.
46509      * @param {Number} row The row index
46510      * @param {Number} col The column index
46511      * @param {Boolean} hscroll false to disable horizontal scrolling
46512      */
46513     focusCell : function(row, col, hscroll){
46514         var el = this.ensureVisible(row, col, hscroll);
46515         this.focusEl.alignTo(el, "tl-tl");
46516         if(Roo.isGecko){
46517             this.focusEl.focus();
46518         }else{
46519             this.focusEl.focus.defer(1, this.focusEl);
46520         }
46521     },
46522
46523     /**
46524      * Scrolls the specified cell into view
46525      * @param {Number} row The row index
46526      * @param {Number} col The column index
46527      * @param {Boolean} hscroll false to disable horizontal scrolling
46528      */
46529     ensureVisible : function(row, col, hscroll){
46530         if(typeof row != "number"){
46531             row = row.rowIndex;
46532         }
46533         if(row < 0 && row >= this.ds.getCount()){
46534             return;
46535         }
46536         col = (col !== undefined ? col : 0);
46537         var cm = this.grid.colModel;
46538         while(cm.isHidden(col)){
46539             col++;
46540         }
46541
46542         var el = this.getCell(row, col);
46543         if(!el){
46544             return;
46545         }
46546         var c = this.scroller.dom;
46547
46548         var ctop = parseInt(el.offsetTop, 10);
46549         var cleft = parseInt(el.offsetLeft, 10);
46550         var cbot = ctop + el.offsetHeight;
46551         var cright = cleft + el.offsetWidth;
46552
46553         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46554         var stop = parseInt(c.scrollTop, 10);
46555         var sleft = parseInt(c.scrollLeft, 10);
46556         var sbot = stop + ch;
46557         var sright = sleft + c.clientWidth;
46558
46559         if(ctop < stop){
46560                 c.scrollTop = ctop;
46561         }else if(cbot > sbot){
46562             c.scrollTop = cbot-ch;
46563         }
46564
46565         if(hscroll !== false){
46566             if(cleft < sleft){
46567                 c.scrollLeft = cleft;
46568             }else if(cright > sright){
46569                 c.scrollLeft = cright-c.clientWidth;
46570             }
46571         }
46572         return el;
46573     },
46574
46575     updateColumns : function(){
46576         this.grid.stopEditing();
46577         var cm = this.grid.colModel, colIds = this.getColumnIds();
46578         //var totalWidth = cm.getTotalWidth();
46579         var pos = 0;
46580         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46581             //if(cm.isHidden(i)) continue;
46582             var w = cm.getColumnWidth(i);
46583             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46584             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46585         }
46586         this.updateSplitters();
46587     },
46588
46589     generateRules : function(cm){
46590         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46591         Roo.util.CSS.removeStyleSheet(rulesId);
46592         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46593             var cid = cm.getColumnId(i);
46594             var align = '';
46595             if(cm.config[i].align){
46596                 align = 'text-align:'+cm.config[i].align+';';
46597             }
46598             var hidden = '';
46599             if(cm.isHidden(i)){
46600                 hidden = 'display:none;';
46601             }
46602             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46603             ruleBuf.push(
46604                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46605                     this.hdSelector, cid, " {\n", align, width, "}\n",
46606                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46607                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46608         }
46609         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46610     },
46611
46612     updateSplitters : function(){
46613         var cm = this.cm, s = this.getSplitters();
46614         if(s){ // splitters not created yet
46615             var pos = 0, locked = true;
46616             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46617                 if(cm.isHidden(i)) continue;
46618                 var w = cm.getColumnWidth(i);
46619                 if(!cm.isLocked(i) && locked){
46620                     pos = 0;
46621                     locked = false;
46622                 }
46623                 pos += w;
46624                 s[i].style.left = (pos-this.splitOffset) + "px";
46625             }
46626         }
46627     },
46628
46629     handleHiddenChange : function(colModel, colIndex, hidden){
46630         if(hidden){
46631             this.hideColumn(colIndex);
46632         }else{
46633             this.unhideColumn(colIndex);
46634         }
46635     },
46636
46637     hideColumn : function(colIndex){
46638         var cid = this.getColumnId(colIndex);
46639         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46640         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46641         if(Roo.isSafari){
46642             this.updateHeaders();
46643         }
46644         this.updateSplitters();
46645         this.layout();
46646     },
46647
46648     unhideColumn : function(colIndex){
46649         var cid = this.getColumnId(colIndex);
46650         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46651         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46652
46653         if(Roo.isSafari){
46654             this.updateHeaders();
46655         }
46656         this.updateSplitters();
46657         this.layout();
46658     },
46659
46660     insertRows : function(dm, firstRow, lastRow, isUpdate){
46661         if(firstRow == 0 && lastRow == dm.getCount()-1){
46662             this.refresh();
46663         }else{
46664             if(!isUpdate){
46665                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46666             }
46667             var s = this.getScrollState();
46668             var markup = this.renderRows(firstRow, lastRow);
46669             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46670             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46671             this.restoreScroll(s);
46672             if(!isUpdate){
46673                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46674                 this.syncRowHeights(firstRow, lastRow);
46675                 this.stripeRows(firstRow);
46676                 this.layout();
46677             }
46678         }
46679     },
46680
46681     bufferRows : function(markup, target, index){
46682         var before = null, trows = target.rows, tbody = target.tBodies[0];
46683         if(index < trows.length){
46684             before = trows[index];
46685         }
46686         var b = document.createElement("div");
46687         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46688         var rows = b.firstChild.rows;
46689         for(var i = 0, len = rows.length; i < len; i++){
46690             if(before){
46691                 tbody.insertBefore(rows[0], before);
46692             }else{
46693                 tbody.appendChild(rows[0]);
46694             }
46695         }
46696         b.innerHTML = "";
46697         b = null;
46698     },
46699
46700     deleteRows : function(dm, firstRow, lastRow){
46701         if(dm.getRowCount()<1){
46702             this.fireEvent("beforerefresh", this);
46703             this.mainBody.update("");
46704             this.lockedBody.update("");
46705             this.fireEvent("refresh", this);
46706         }else{
46707             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46708             var bt = this.getBodyTable();
46709             var tbody = bt.firstChild;
46710             var rows = bt.rows;
46711             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46712                 tbody.removeChild(rows[firstRow]);
46713             }
46714             this.stripeRows(firstRow);
46715             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46716         }
46717     },
46718
46719     updateRows : function(dataSource, firstRow, lastRow){
46720         var s = this.getScrollState();
46721         this.refresh();
46722         this.restoreScroll(s);
46723     },
46724
46725     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46726         if(!noRefresh){
46727            this.refresh();
46728         }
46729         this.updateHeaderSortState();
46730     },
46731
46732     getScrollState : function(){
46733         var sb = this.scroller.dom;
46734         return {left: sb.scrollLeft, top: sb.scrollTop};
46735     },
46736
46737     stripeRows : function(startRow){
46738         if(!this.grid.stripeRows || this.ds.getCount() < 1){
46739             return;
46740         }
46741         startRow = startRow || 0;
46742         var rows = this.getBodyTable().rows;
46743         var lrows = this.getLockedTable().rows;
46744         var cls = ' x-grid-row-alt ';
46745         for(var i = startRow, len = rows.length; i < len; i++){
46746             var row = rows[i], lrow = lrows[i];
46747             var isAlt = ((i+1) % 2 == 0);
46748             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
46749             if(isAlt == hasAlt){
46750                 continue;
46751             }
46752             if(isAlt){
46753                 row.className += " x-grid-row-alt";
46754             }else{
46755                 row.className = row.className.replace("x-grid-row-alt", "");
46756             }
46757             if(lrow){
46758                 lrow.className = row.className;
46759             }
46760         }
46761     },
46762
46763     restoreScroll : function(state){
46764         var sb = this.scroller.dom;
46765         sb.scrollLeft = state.left;
46766         sb.scrollTop = state.top;
46767         this.syncScroll();
46768     },
46769
46770     syncScroll : function(){
46771         var sb = this.scroller.dom;
46772         var sh = this.mainHd.dom;
46773         var bs = this.mainBody.dom;
46774         var lv = this.lockedBody.dom;
46775         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
46776         lv.scrollTop = bs.scrollTop = sb.scrollTop;
46777     },
46778
46779     handleScroll : function(e){
46780         this.syncScroll();
46781         var sb = this.scroller.dom;
46782         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
46783         e.stopEvent();
46784     },
46785
46786     handleWheel : function(e){
46787         var d = e.getWheelDelta();
46788         this.scroller.dom.scrollTop -= d*22;
46789         // set this here to prevent jumpy scrolling on large tables
46790         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
46791         e.stopEvent();
46792     },
46793
46794     renderRows : function(startRow, endRow){
46795         // pull in all the crap needed to render rows
46796         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
46797         var colCount = cm.getColumnCount();
46798
46799         if(ds.getCount() < 1){
46800             return ["", ""];
46801         }
46802
46803         // build a map for all the columns
46804         var cs = [];
46805         for(var i = 0; i < colCount; i++){
46806             var name = cm.getDataIndex(i);
46807             cs[i] = {
46808                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
46809                 renderer : cm.getRenderer(i),
46810                 id : cm.getColumnId(i),
46811                 locked : cm.isLocked(i)
46812             };
46813         }
46814
46815         startRow = startRow || 0;
46816         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
46817
46818         // records to render
46819         var rs = ds.getRange(startRow, endRow);
46820
46821         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
46822     },
46823
46824     // As much as I hate to duplicate code, this was branched because FireFox really hates
46825     // [].join("") on strings. The performance difference was substantial enough to
46826     // branch this function
46827     doRender : Roo.isGecko ?
46828             function(cs, rs, ds, startRow, colCount, stripe){
46829                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46830                 // buffers
46831                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46832                 for(var j = 0, len = rs.length; j < len; j++){
46833                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
46834                     for(var i = 0; i < colCount; i++){
46835                         c = cs[i];
46836                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46837                         p.id = c.id;
46838                         p.css = p.attr = "";
46839                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46840                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46841                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46842                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46843                         }
46844                         var markup = ct.apply(p);
46845                         if(!c.locked){
46846                             cb+= markup;
46847                         }else{
46848                             lcb+= markup;
46849                         }
46850                     }
46851                     var alt = [];
46852                     if(stripe && ((rowIndex+1) % 2 == 0)){
46853                         alt[0] = "x-grid-row-alt";
46854                     }
46855                     if(r.dirty){
46856                         alt[1] = " x-grid-dirty-row";
46857                     }
46858                     rp.cells = lcb;
46859                     if(this.getRowClass){
46860                         alt[2] = this.getRowClass(r, rowIndex);
46861                     }
46862                     rp.alt = alt.join(" ");
46863                     lbuf+= rt.apply(rp);
46864                     rp.cells = cb;
46865                     buf+=  rt.apply(rp);
46866                 }
46867                 return [lbuf, buf];
46868             } :
46869             function(cs, rs, ds, startRow, colCount, stripe){
46870                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46871                 // buffers
46872                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46873                 for(var j = 0, len = rs.length; j < len; j++){
46874                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
46875                     for(var i = 0; i < colCount; i++){
46876                         c = cs[i];
46877                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46878                         p.id = c.id;
46879                         p.css = p.attr = "";
46880                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46881                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46882                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46883                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46884                         }
46885                         var markup = ct.apply(p);
46886                         if(!c.locked){
46887                             cb[cb.length] = markup;
46888                         }else{
46889                             lcb[lcb.length] = markup;
46890                         }
46891                     }
46892                     var alt = [];
46893                     if(stripe && ((rowIndex+1) % 2 == 0)){
46894                         alt[0] = "x-grid-row-alt";
46895                     }
46896                     if(r.dirty){
46897                         alt[1] = " x-grid-dirty-row";
46898                     }
46899                     rp.cells = lcb;
46900                     if(this.getRowClass){
46901                         alt[2] = this.getRowClass(r, rowIndex);
46902                     }
46903                     rp.alt = alt.join(" ");
46904                     rp.cells = lcb.join("");
46905                     lbuf[lbuf.length] = rt.apply(rp);
46906                     rp.cells = cb.join("");
46907                     buf[buf.length] =  rt.apply(rp);
46908                 }
46909                 return [lbuf.join(""), buf.join("")];
46910             },
46911
46912     renderBody : function(){
46913         var markup = this.renderRows();
46914         var bt = this.templates.body;
46915         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
46916     },
46917
46918     /**
46919      * Refreshes the grid
46920      * @param {Boolean} headersToo
46921      */
46922     refresh : function(headersToo){
46923         this.fireEvent("beforerefresh", this);
46924         this.grid.stopEditing();
46925         var result = this.renderBody();
46926         this.lockedBody.update(result[0]);
46927         this.mainBody.update(result[1]);
46928         if(headersToo === true){
46929             this.updateHeaders();
46930             this.updateColumns();
46931             this.updateSplitters();
46932             this.updateHeaderSortState();
46933         }
46934         this.syncRowHeights();
46935         this.layout();
46936         this.fireEvent("refresh", this);
46937     },
46938
46939     handleColumnMove : function(cm, oldIndex, newIndex){
46940         this.indexMap = null;
46941         var s = this.getScrollState();
46942         this.refresh(true);
46943         this.restoreScroll(s);
46944         this.afterMove(newIndex);
46945     },
46946
46947     afterMove : function(colIndex){
46948         if(this.enableMoveAnim && Roo.enableFx){
46949             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
46950         }
46951     },
46952
46953     updateCell : function(dm, rowIndex, dataIndex){
46954         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
46955         if(typeof colIndex == "undefined"){ // not present in grid
46956             return;
46957         }
46958         var cm = this.grid.colModel;
46959         var cell = this.getCell(rowIndex, colIndex);
46960         var cellText = this.getCellText(rowIndex, colIndex);
46961
46962         var p = {
46963             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
46964             id : cm.getColumnId(colIndex),
46965             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
46966         };
46967         var renderer = cm.getRenderer(colIndex);
46968         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
46969         if(typeof val == "undefined" || val === "") val = "&#160;";
46970         cellText.innerHTML = val;
46971         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
46972         this.syncRowHeights(rowIndex, rowIndex);
46973     },
46974
46975     calcColumnWidth : function(colIndex, maxRowsToMeasure){
46976         var maxWidth = 0;
46977         if(this.grid.autoSizeHeaders){
46978             var h = this.getHeaderCellMeasure(colIndex);
46979             maxWidth = Math.max(maxWidth, h.scrollWidth);
46980         }
46981         var tb, index;
46982         if(this.cm.isLocked(colIndex)){
46983             tb = this.getLockedTable();
46984             index = colIndex;
46985         }else{
46986             tb = this.getBodyTable();
46987             index = colIndex - this.cm.getLockedCount();
46988         }
46989         if(tb && tb.rows){
46990             var rows = tb.rows;
46991             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
46992             for(var i = 0; i < stopIndex; i++){
46993                 var cell = rows[i].childNodes[index].firstChild;
46994                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
46995             }
46996         }
46997         return maxWidth + /*margin for error in IE*/ 5;
46998     },
46999     /**
47000      * Autofit a column to its content.
47001      * @param {Number} colIndex
47002      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47003      */
47004      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47005          if(this.cm.isHidden(colIndex)){
47006              return; // can't calc a hidden column
47007          }
47008         if(forceMinSize){
47009             var cid = this.cm.getColumnId(colIndex);
47010             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47011            if(this.grid.autoSizeHeaders){
47012                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47013            }
47014         }
47015         var newWidth = this.calcColumnWidth(colIndex);
47016         this.cm.setColumnWidth(colIndex,
47017             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47018         if(!suppressEvent){
47019             this.grid.fireEvent("columnresize", colIndex, newWidth);
47020         }
47021     },
47022
47023     /**
47024      * Autofits all columns to their content and then expands to fit any extra space in the grid
47025      */
47026      autoSizeColumns : function(){
47027         var cm = this.grid.colModel;
47028         var colCount = cm.getColumnCount();
47029         for(var i = 0; i < colCount; i++){
47030             this.autoSizeColumn(i, true, true);
47031         }
47032         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47033             this.fitColumns();
47034         }else{
47035             this.updateColumns();
47036             this.layout();
47037         }
47038     },
47039
47040     /**
47041      * Autofits all columns to the grid's width proportionate with their current size
47042      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47043      */
47044     fitColumns : function(reserveScrollSpace){
47045         var cm = this.grid.colModel;
47046         var colCount = cm.getColumnCount();
47047         var cols = [];
47048         var width = 0;
47049         var i, w;
47050         for (i = 0; i < colCount; i++){
47051             if(!cm.isHidden(i) && !cm.isFixed(i)){
47052                 w = cm.getColumnWidth(i);
47053                 cols.push(i);
47054                 cols.push(w);
47055                 width += w;
47056             }
47057         }
47058         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47059         if(reserveScrollSpace){
47060             avail -= 17;
47061         }
47062         var frac = (avail - cm.getTotalWidth())/width;
47063         while (cols.length){
47064             w = cols.pop();
47065             i = cols.pop();
47066             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47067         }
47068         this.updateColumns();
47069         this.layout();
47070     },
47071
47072     onRowSelect : function(rowIndex){
47073         var row = this.getRowComposite(rowIndex);
47074         row.addClass("x-grid-row-selected");
47075     },
47076
47077     onRowDeselect : function(rowIndex){
47078         var row = this.getRowComposite(rowIndex);
47079         row.removeClass("x-grid-row-selected");
47080     },
47081
47082     onCellSelect : function(row, col){
47083         var cell = this.getCell(row, col);
47084         if(cell){
47085             Roo.fly(cell).addClass("x-grid-cell-selected");
47086         }
47087     },
47088
47089     onCellDeselect : function(row, col){
47090         var cell = this.getCell(row, col);
47091         if(cell){
47092             Roo.fly(cell).removeClass("x-grid-cell-selected");
47093         }
47094     },
47095
47096     updateHeaderSortState : function(){
47097         var state = this.ds.getSortState();
47098         if(!state){
47099             return;
47100         }
47101         this.sortState = state;
47102         var sortColumn = this.cm.findColumnIndex(state.field);
47103         if(sortColumn != -1){
47104             var sortDir = state.direction;
47105             var sc = this.sortClasses;
47106             var hds = this.el.select(this.headerSelector).removeClass(sc);
47107             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47108         }
47109     },
47110
47111     handleHeaderClick : function(g, index){
47112         if(this.headersDisabled){
47113             return;
47114         }
47115         var dm = g.dataSource, cm = g.colModel;
47116             if(!cm.isSortable(index)){
47117             return;
47118         }
47119             g.stopEditing();
47120         dm.sort(cm.getDataIndex(index));
47121     },
47122
47123
47124     destroy : function(){
47125         if(this.colMenu){
47126             this.colMenu.removeAll();
47127             Roo.menu.MenuMgr.unregister(this.colMenu);
47128             this.colMenu.getEl().remove();
47129             delete this.colMenu;
47130         }
47131         if(this.hmenu){
47132             this.hmenu.removeAll();
47133             Roo.menu.MenuMgr.unregister(this.hmenu);
47134             this.hmenu.getEl().remove();
47135             delete this.hmenu;
47136         }
47137         if(this.grid.enableColumnMove){
47138             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47139             if(dds){
47140                 for(var dd in dds){
47141                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47142                         var elid = dds[dd].dragElId;
47143                         dds[dd].unreg();
47144                         Roo.get(elid).remove();
47145                     } else if(dds[dd].config.isTarget){
47146                         dds[dd].proxyTop.remove();
47147                         dds[dd].proxyBottom.remove();
47148                         dds[dd].unreg();
47149                     }
47150                     if(Roo.dd.DDM.locationCache[dd]){
47151                         delete Roo.dd.DDM.locationCache[dd];
47152                     }
47153                 }
47154                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47155             }
47156         }
47157         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47158         this.bind(null, null);
47159         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47160     },
47161
47162     handleLockChange : function(){
47163         this.refresh(true);
47164     },
47165
47166     onDenyColumnLock : function(){
47167
47168     },
47169
47170     onDenyColumnHide : function(){
47171
47172     },
47173
47174     handleHdMenuClick : function(item){
47175         var index = this.hdCtxIndex;
47176         var cm = this.cm, ds = this.ds;
47177         switch(item.id){
47178             case "asc":
47179                 ds.sort(cm.getDataIndex(index), "ASC");
47180                 break;
47181             case "desc":
47182                 ds.sort(cm.getDataIndex(index), "DESC");
47183                 break;
47184             case "lock":
47185                 var lc = cm.getLockedCount();
47186                 if(cm.getColumnCount(true) <= lc+1){
47187                     this.onDenyColumnLock();
47188                     return;
47189                 }
47190                 if(lc != index){
47191                     cm.setLocked(index, true, true);
47192                     cm.moveColumn(index, lc);
47193                     this.grid.fireEvent("columnmove", index, lc);
47194                 }else{
47195                     cm.setLocked(index, true);
47196                 }
47197             break;
47198             case "unlock":
47199                 var lc = cm.getLockedCount();
47200                 if((lc-1) != index){
47201                     cm.setLocked(index, false, true);
47202                     cm.moveColumn(index, lc-1);
47203                     this.grid.fireEvent("columnmove", index, lc-1);
47204                 }else{
47205                     cm.setLocked(index, false);
47206                 }
47207             break;
47208             default:
47209                 index = cm.getIndexById(item.id.substr(4));
47210                 if(index != -1){
47211                     if(item.checked && cm.getColumnCount(true) <= 1){
47212                         this.onDenyColumnHide();
47213                         return false;
47214                     }
47215                     cm.setHidden(index, item.checked);
47216                 }
47217         }
47218         return true;
47219     },
47220
47221     beforeColMenuShow : function(){
47222         var cm = this.cm,  colCount = cm.getColumnCount();
47223         this.colMenu.removeAll();
47224         for(var i = 0; i < colCount; i++){
47225             this.colMenu.add(new Roo.menu.CheckItem({
47226                 id: "col-"+cm.getColumnId(i),
47227                 text: cm.getColumnHeader(i),
47228                 checked: !cm.isHidden(i),
47229                 hideOnClick:false
47230             }));
47231         }
47232     },
47233
47234     handleHdCtx : function(g, index, e){
47235         e.stopEvent();
47236         var hd = this.getHeaderCell(index);
47237         this.hdCtxIndex = index;
47238         var ms = this.hmenu.items, cm = this.cm;
47239         ms.get("asc").setDisabled(!cm.isSortable(index));
47240         ms.get("desc").setDisabled(!cm.isSortable(index));
47241         if(this.grid.enableColLock !== false){
47242             ms.get("lock").setDisabled(cm.isLocked(index));
47243             ms.get("unlock").setDisabled(!cm.isLocked(index));
47244         }
47245         this.hmenu.show(hd, "tl-bl");
47246     },
47247
47248     handleHdOver : function(e){
47249         var hd = this.findHeaderCell(e.getTarget());
47250         if(hd && !this.headersDisabled){
47251             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47252                this.fly(hd).addClass("x-grid-hd-over");
47253             }
47254         }
47255     },
47256
47257     handleHdOut : function(e){
47258         var hd = this.findHeaderCell(e.getTarget());
47259         if(hd){
47260             this.fly(hd).removeClass("x-grid-hd-over");
47261         }
47262     },
47263
47264     handleSplitDblClick : function(e, t){
47265         var i = this.getCellIndex(t);
47266         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47267             this.autoSizeColumn(i, true);
47268             this.layout();
47269         }
47270     },
47271
47272     render : function(){
47273
47274         var cm = this.cm;
47275         var colCount = cm.getColumnCount();
47276
47277         if(this.grid.monitorWindowResize === true){
47278             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47279         }
47280         var header = this.renderHeaders();
47281         var body = this.templates.body.apply({rows:""});
47282         var html = this.templates.master.apply({
47283             lockedBody: body,
47284             body: body,
47285             lockedHeader: header[0],
47286             header: header[1]
47287         });
47288
47289         //this.updateColumns();
47290
47291         this.grid.getGridEl().dom.innerHTML = html;
47292
47293         this.initElements();
47294
47295         this.scroller.on("scroll", this.handleScroll, this);
47296         this.lockedBody.on("mousewheel", this.handleWheel, this);
47297         this.mainBody.on("mousewheel", this.handleWheel, this);
47298
47299         this.mainHd.on("mouseover", this.handleHdOver, this);
47300         this.mainHd.on("mouseout", this.handleHdOut, this);
47301         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47302                 {delegate: "."+this.splitClass});
47303
47304         this.lockedHd.on("mouseover", this.handleHdOver, this);
47305         this.lockedHd.on("mouseout", this.handleHdOut, this);
47306         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47307                 {delegate: "."+this.splitClass});
47308
47309         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47310             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47311         }
47312
47313         this.updateSplitters();
47314
47315         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47316             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47317             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47318         }
47319
47320         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47321             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47322             this.hmenu.add(
47323                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47324                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47325             );
47326             if(this.grid.enableColLock !== false){
47327                 this.hmenu.add('-',
47328                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47329                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47330                 );
47331             }
47332             if(this.grid.enableColumnHide !== false){
47333
47334                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47335                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47336                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47337
47338                 this.hmenu.add('-',
47339                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47340                 );
47341             }
47342             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47343
47344             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47345         }
47346
47347         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47348             this.dd = new Roo.grid.GridDragZone(this.grid, {
47349                 ddGroup : this.grid.ddGroup || 'GridDD'
47350             });
47351         }
47352
47353         /*
47354         for(var i = 0; i < colCount; i++){
47355             if(cm.isHidden(i)){
47356                 this.hideColumn(i);
47357             }
47358             if(cm.config[i].align){
47359                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47360                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47361             }
47362         }*/
47363         
47364         this.updateHeaderSortState();
47365
47366         this.beforeInitialResize();
47367         this.layout(true);
47368
47369         // two part rendering gives faster view to the user
47370         this.renderPhase2.defer(1, this);
47371     },
47372
47373     renderPhase2 : function(){
47374         // render the rows now
47375         this.refresh();
47376         if(this.grid.autoSizeColumns){
47377             this.autoSizeColumns();
47378         }
47379     },
47380
47381     beforeInitialResize : function(){
47382
47383     },
47384
47385     onColumnSplitterMoved : function(i, w){
47386         this.userResized = true;
47387         var cm = this.grid.colModel;
47388         cm.setColumnWidth(i, w, true);
47389         var cid = cm.getColumnId(i);
47390         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47391         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47392         this.updateSplitters();
47393         this.layout();
47394         this.grid.fireEvent("columnresize", i, w);
47395     },
47396
47397     syncRowHeights : function(startIndex, endIndex){
47398         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47399             startIndex = startIndex || 0;
47400             var mrows = this.getBodyTable().rows;
47401             var lrows = this.getLockedTable().rows;
47402             var len = mrows.length-1;
47403             endIndex = Math.min(endIndex || len, len);
47404             for(var i = startIndex; i <= endIndex; i++){
47405                 var m = mrows[i], l = lrows[i];
47406                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47407                 m.style.height = l.style.height = h + "px";
47408             }
47409         }
47410     },
47411
47412     layout : function(initialRender, is2ndPass){
47413         var g = this.grid;
47414         var auto = g.autoHeight;
47415         var scrollOffset = 16;
47416         var c = g.getGridEl(), cm = this.cm,
47417                 expandCol = g.autoExpandColumn,
47418                 gv = this;
47419         //c.beginMeasure();
47420
47421         if(!c.dom.offsetWidth){ // display:none?
47422             if(initialRender){
47423                 this.lockedWrap.show();
47424                 this.mainWrap.show();
47425             }
47426             return;
47427         }
47428
47429         var hasLock = this.cm.isLocked(0);
47430
47431         var tbh = this.headerPanel.getHeight();
47432         var bbh = this.footerPanel.getHeight();
47433
47434         if(auto){
47435             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47436             var newHeight = ch + c.getBorderWidth("tb");
47437             if(g.maxHeight){
47438                 newHeight = Math.min(g.maxHeight, newHeight);
47439             }
47440             c.setHeight(newHeight);
47441         }
47442
47443         if(g.autoWidth){
47444             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47445         }
47446
47447         var s = this.scroller;
47448
47449         var csize = c.getSize(true);
47450
47451         this.el.setSize(csize.width, csize.height);
47452
47453         this.headerPanel.setWidth(csize.width);
47454         this.footerPanel.setWidth(csize.width);
47455
47456         var hdHeight = this.mainHd.getHeight();
47457         var vw = csize.width;
47458         var vh = csize.height - (tbh + bbh);
47459
47460         s.setSize(vw, vh);
47461
47462         var bt = this.getBodyTable();
47463         var ltWidth = hasLock ?
47464                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47465
47466         var scrollHeight = bt.offsetHeight;
47467         var scrollWidth = ltWidth + bt.offsetWidth;
47468         var vscroll = false, hscroll = false;
47469
47470         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47471
47472         var lw = this.lockedWrap, mw = this.mainWrap;
47473         var lb = this.lockedBody, mb = this.mainBody;
47474
47475         setTimeout(function(){
47476             var t = s.dom.offsetTop;
47477             var w = s.dom.clientWidth,
47478                 h = s.dom.clientHeight;
47479
47480             lw.setTop(t);
47481             lw.setSize(ltWidth, h);
47482
47483             mw.setLeftTop(ltWidth, t);
47484             mw.setSize(w-ltWidth, h);
47485
47486             lb.setHeight(h-hdHeight);
47487             mb.setHeight(h-hdHeight);
47488
47489             if(is2ndPass !== true && !gv.userResized && expandCol){
47490                 // high speed resize without full column calculation
47491                 
47492                 var ci = cm.getIndexById(expandCol);
47493                 if (ci < 0) {
47494                     ci = cm.findColumnIndex(expandCol);
47495                 }
47496                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47497                 var expandId = cm.getColumnId(ci);
47498                 var  tw = cm.getTotalWidth(false);
47499                 var currentWidth = cm.getColumnWidth(ci);
47500                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47501                 if(currentWidth != cw){
47502                     cm.setColumnWidth(ci, cw, true);
47503                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47504                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47505                     gv.updateSplitters();
47506                     gv.layout(false, true);
47507                 }
47508             }
47509
47510             if(initialRender){
47511                 lw.show();
47512                 mw.show();
47513             }
47514             //c.endMeasure();
47515         }, 10);
47516     },
47517
47518     onWindowResize : function(){
47519         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47520             return;
47521         }
47522         this.layout();
47523     },
47524
47525     appendFooter : function(parentEl){
47526         return null;
47527     },
47528
47529     sortAscText : "Sort Ascending",
47530     sortDescText : "Sort Descending",
47531     lockText : "Lock Column",
47532     unlockText : "Unlock Column",
47533     columnsText : "Columns"
47534 });
47535
47536
47537 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47538     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47539     this.proxy.el.addClass('x-grid3-col-dd');
47540 };
47541
47542 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47543     handleMouseDown : function(e){
47544
47545     },
47546
47547     callHandleMouseDown : function(e){
47548         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47549     }
47550 });
47551 /*
47552  * Based on:
47553  * Ext JS Library 1.1.1
47554  * Copyright(c) 2006-2007, Ext JS, LLC.
47555  *
47556  * Originally Released Under LGPL - original licence link has changed is not relivant.
47557  *
47558  * Fork - LGPL
47559  * <script type="text/javascript">
47560  */
47561  
47562 // private
47563 // This is a support class used internally by the Grid components
47564 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47565     this.grid = grid;
47566     this.view = grid.getView();
47567     this.proxy = this.view.resizeProxy;
47568     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47569         "gridSplitters" + this.grid.getGridEl().id, {
47570         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47571     });
47572     this.setHandleElId(Roo.id(hd));
47573     this.setOuterHandleElId(Roo.id(hd2));
47574     this.scroll = false;
47575 };
47576 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47577     fly: Roo.Element.fly,
47578
47579     b4StartDrag : function(x, y){
47580         this.view.headersDisabled = true;
47581         this.proxy.setHeight(this.view.mainWrap.getHeight());
47582         var w = this.cm.getColumnWidth(this.cellIndex);
47583         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47584         this.resetConstraints();
47585         this.setXConstraint(minw, 1000);
47586         this.setYConstraint(0, 0);
47587         this.minX = x - minw;
47588         this.maxX = x + 1000;
47589         this.startPos = x;
47590         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47591     },
47592
47593
47594     handleMouseDown : function(e){
47595         ev = Roo.EventObject.setEvent(e);
47596         var t = this.fly(ev.getTarget());
47597         if(t.hasClass("x-grid-split")){
47598             this.cellIndex = this.view.getCellIndex(t.dom);
47599             this.split = t.dom;
47600             this.cm = this.grid.colModel;
47601             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47602                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47603             }
47604         }
47605     },
47606
47607     endDrag : function(e){
47608         this.view.headersDisabled = false;
47609         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47610         var diff = endX - this.startPos;
47611         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47612     },
47613
47614     autoOffset : function(){
47615         this.setDelta(0,0);
47616     }
47617 });/*
47618  * Based on:
47619  * Ext JS Library 1.1.1
47620  * Copyright(c) 2006-2007, Ext JS, LLC.
47621  *
47622  * Originally Released Under LGPL - original licence link has changed is not relivant.
47623  *
47624  * Fork - LGPL
47625  * <script type="text/javascript">
47626  */
47627  
47628 // private
47629 // This is a support class used internally by the Grid components
47630 Roo.grid.GridDragZone = function(grid, config){
47631     this.view = grid.getView();
47632     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47633     if(this.view.lockedBody){
47634         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47635         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47636     }
47637     this.scroll = false;
47638     this.grid = grid;
47639     this.ddel = document.createElement('div');
47640     this.ddel.className = 'x-grid-dd-wrap';
47641 };
47642
47643 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47644     ddGroup : "GridDD",
47645
47646     getDragData : function(e){
47647         var t = Roo.lib.Event.getTarget(e);
47648         var rowIndex = this.view.findRowIndex(t);
47649         if(rowIndex !== false){
47650             var sm = this.grid.selModel;
47651             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47652               //  sm.mouseDown(e, t);
47653             //}
47654             if (e.hasModifier()){
47655                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47656             }
47657             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47658         }
47659         return false;
47660     },
47661
47662     onInitDrag : function(e){
47663         var data = this.dragData;
47664         this.ddel.innerHTML = this.grid.getDragDropText();
47665         this.proxy.update(this.ddel);
47666         // fire start drag?
47667     },
47668
47669     afterRepair : function(){
47670         this.dragging = false;
47671     },
47672
47673     getRepairXY : function(e, data){
47674         return false;
47675     },
47676
47677     onEndDrag : function(data, e){
47678         // fire end drag?
47679     },
47680
47681     onValidDrop : function(dd, e, id){
47682         // fire drag drop?
47683         this.hideProxy();
47684     },
47685
47686     beforeInvalidDrop : function(e, id){
47687
47688     }
47689 });/*
47690  * Based on:
47691  * Ext JS Library 1.1.1
47692  * Copyright(c) 2006-2007, Ext JS, LLC.
47693  *
47694  * Originally Released Under LGPL - original licence link has changed is not relivant.
47695  *
47696  * Fork - LGPL
47697  * <script type="text/javascript">
47698  */
47699  
47700
47701 /**
47702  * @class Roo.grid.ColumnModel
47703  * @extends Roo.util.Observable
47704  * This is the default implementation of a ColumnModel used by the Grid. It defines
47705  * the columns in the grid.
47706  * <br>Usage:<br>
47707  <pre><code>
47708  var colModel = new Roo.grid.ColumnModel([
47709         {header: "Ticker", width: 60, sortable: true, locked: true},
47710         {header: "Company Name", width: 150, sortable: true},
47711         {header: "Market Cap.", width: 100, sortable: true},
47712         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47713         {header: "Employees", width: 100, sortable: true, resizable: false}
47714  ]);
47715  </code></pre>
47716  * <p>
47717  
47718  * The config options listed for this class are options which may appear in each
47719  * individual column definition.
47720  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47721  * @constructor
47722  * @param {Object} config An Array of column config objects. See this class's
47723  * config objects for details.
47724 */
47725 Roo.grid.ColumnModel = function(config){
47726         /**
47727      * The config passed into the constructor
47728      */
47729     this.config = config;
47730     this.lookup = {};
47731
47732     // if no id, create one
47733     // if the column does not have a dataIndex mapping,
47734     // map it to the order it is in the config
47735     for(var i = 0, len = config.length; i < len; i++){
47736         var c = config[i];
47737         if(typeof c.dataIndex == "undefined"){
47738             c.dataIndex = i;
47739         }
47740         if(typeof c.renderer == "string"){
47741             c.renderer = Roo.util.Format[c.renderer];
47742         }
47743         if(typeof c.id == "undefined"){
47744             c.id = Roo.id();
47745         }
47746         if(c.editor && c.editor.xtype){
47747             c.editor  = Roo.factory(c.editor, Roo.grid);
47748         }
47749         if(c.editor && c.editor.isFormField){
47750             c.editor = new Roo.grid.GridEditor(c.editor);
47751         }
47752         this.lookup[c.id] = c;
47753     }
47754
47755     /**
47756      * The width of columns which have no width specified (defaults to 100)
47757      * @type Number
47758      */
47759     this.defaultWidth = 100;
47760
47761     /**
47762      * Default sortable of columns which have no sortable specified (defaults to false)
47763      * @type Boolean
47764      */
47765     this.defaultSortable = false;
47766
47767     this.addEvents({
47768         /**
47769              * @event widthchange
47770              * Fires when the width of a column changes.
47771              * @param {ColumnModel} this
47772              * @param {Number} columnIndex The column index
47773              * @param {Number} newWidth The new width
47774              */
47775             "widthchange": true,
47776         /**
47777              * @event headerchange
47778              * Fires when the text of a header changes.
47779              * @param {ColumnModel} this
47780              * @param {Number} columnIndex The column index
47781              * @param {Number} newText The new header text
47782              */
47783             "headerchange": true,
47784         /**
47785              * @event hiddenchange
47786              * Fires when a column is hidden or "unhidden".
47787              * @param {ColumnModel} this
47788              * @param {Number} columnIndex The column index
47789              * @param {Boolean} hidden true if hidden, false otherwise
47790              */
47791             "hiddenchange": true,
47792             /**
47793          * @event columnmoved
47794          * Fires when a column is moved.
47795          * @param {ColumnModel} this
47796          * @param {Number} oldIndex
47797          * @param {Number} newIndex
47798          */
47799         "columnmoved" : true,
47800         /**
47801          * @event columlockchange
47802          * Fires when a column's locked state is changed
47803          * @param {ColumnModel} this
47804          * @param {Number} colIndex
47805          * @param {Boolean} locked true if locked
47806          */
47807         "columnlockchange" : true
47808     });
47809     Roo.grid.ColumnModel.superclass.constructor.call(this);
47810 };
47811 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
47812     /**
47813      * @cfg {String} header The header text to display in the Grid view.
47814      */
47815     /**
47816      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
47817      * {@link Roo.data.Record} definition from which to draw the column's value. If not
47818      * specified, the column's index is used as an index into the Record's data Array.
47819      */
47820     /**
47821      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
47822      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
47823      */
47824     /**
47825      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
47826      * Defaults to the value of the {@link #defaultSortable} property.
47827      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
47828      */
47829     /**
47830      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
47831      */
47832     /**
47833      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
47834      */
47835     /**
47836      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
47837      */
47838     /**
47839      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
47840      */
47841     /**
47842      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
47843      * given the cell's data value. See {@link #setRenderer}. If not specified, the
47844      * default renderer uses the raw data value.
47845      */
47846        /**
47847      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
47848      */
47849     /**
47850      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
47851      */
47852
47853     /**
47854      * Returns the id of the column at the specified index.
47855      * @param {Number} index The column index
47856      * @return {String} the id
47857      */
47858     getColumnId : function(index){
47859         return this.config[index].id;
47860     },
47861
47862     /**
47863      * Returns the column for a specified id.
47864      * @param {String} id The column id
47865      * @return {Object} the column
47866      */
47867     getColumnById : function(id){
47868         return this.lookup[id];
47869     },
47870
47871     /**
47872      * Returns the index for a specified column id.
47873      * @param {String} id The column id
47874      * @return {Number} the index, or -1 if not found
47875      */
47876     getIndexById : function(id){
47877         for(var i = 0, len = this.config.length; i < len; i++){
47878             if(this.config[i].id == id){
47879                 return i;
47880             }
47881         }
47882         return -1;
47883     },
47884     /**
47885      * Returns the index for a specified column dataIndex.
47886      * @param {String} dataIndex The column dataIndex
47887      * @return {Number} the index, or -1 if not found
47888      */
47889     
47890     findColumnIndex : function(dataIndex){
47891         for(var i = 0, len = this.config.length; i < len; i++){
47892             if(this.config[i].dataIndex == dataIndex){
47893                 return i;
47894             }
47895         }
47896         return -1;
47897     },
47898     
47899     
47900     moveColumn : function(oldIndex, newIndex){
47901         var c = this.config[oldIndex];
47902         this.config.splice(oldIndex, 1);
47903         this.config.splice(newIndex, 0, c);
47904         this.dataMap = null;
47905         this.fireEvent("columnmoved", this, oldIndex, newIndex);
47906     },
47907
47908     isLocked : function(colIndex){
47909         return this.config[colIndex].locked === true;
47910     },
47911
47912     setLocked : function(colIndex, value, suppressEvent){
47913         if(this.isLocked(colIndex) == value){
47914             return;
47915         }
47916         this.config[colIndex].locked = value;
47917         if(!suppressEvent){
47918             this.fireEvent("columnlockchange", this, colIndex, value);
47919         }
47920     },
47921
47922     getTotalLockedWidth : function(){
47923         var totalWidth = 0;
47924         for(var i = 0; i < this.config.length; i++){
47925             if(this.isLocked(i) && !this.isHidden(i)){
47926                 this.totalWidth += this.getColumnWidth(i);
47927             }
47928         }
47929         return totalWidth;
47930     },
47931
47932     getLockedCount : function(){
47933         for(var i = 0, len = this.config.length; i < len; i++){
47934             if(!this.isLocked(i)){
47935                 return i;
47936             }
47937         }
47938     },
47939
47940     /**
47941      * Returns the number of columns.
47942      * @return {Number}
47943      */
47944     getColumnCount : function(visibleOnly){
47945         if(visibleOnly === true){
47946             var c = 0;
47947             for(var i = 0, len = this.config.length; i < len; i++){
47948                 if(!this.isHidden(i)){
47949                     c++;
47950                 }
47951             }
47952             return c;
47953         }
47954         return this.config.length;
47955     },
47956
47957     /**
47958      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
47959      * @param {Function} fn
47960      * @param {Object} scope (optional)
47961      * @return {Array} result
47962      */
47963     getColumnsBy : function(fn, scope){
47964         var r = [];
47965         for(var i = 0, len = this.config.length; i < len; i++){
47966             var c = this.config[i];
47967             if(fn.call(scope||this, c, i) === true){
47968                 r[r.length] = c;
47969             }
47970         }
47971         return r;
47972     },
47973
47974     /**
47975      * Returns true if the specified column is sortable.
47976      * @param {Number} col The column index
47977      * @return {Boolean}
47978      */
47979     isSortable : function(col){
47980         if(typeof this.config[col].sortable == "undefined"){
47981             return this.defaultSortable;
47982         }
47983         return this.config[col].sortable;
47984     },
47985
47986     /**
47987      * Returns the rendering (formatting) function defined for the column.
47988      * @param {Number} col The column index.
47989      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
47990      */
47991     getRenderer : function(col){
47992         if(!this.config[col].renderer){
47993             return Roo.grid.ColumnModel.defaultRenderer;
47994         }
47995         return this.config[col].renderer;
47996     },
47997
47998     /**
47999      * Sets the rendering (formatting) function for a column.
48000      * @param {Number} col The column index
48001      * @param {Function} fn The function to use to process the cell's raw data
48002      * to return HTML markup for the grid view. The render function is called with
48003      * the following parameters:<ul>
48004      * <li>Data value.</li>
48005      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48006      * <li>css A CSS style string to apply to the table cell.</li>
48007      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48008      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48009      * <li>Row index</li>
48010      * <li>Column index</li>
48011      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48012      */
48013     setRenderer : function(col, fn){
48014         this.config[col].renderer = fn;
48015     },
48016
48017     /**
48018      * Returns the width for the specified column.
48019      * @param {Number} col The column index
48020      * @return {Number}
48021      */
48022     getColumnWidth : function(col){
48023         return this.config[col].width || this.defaultWidth;
48024     },
48025
48026     /**
48027      * Sets the width for a column.
48028      * @param {Number} col The column index
48029      * @param {Number} width The new width
48030      */
48031     setColumnWidth : function(col, width, suppressEvent){
48032         this.config[col].width = width;
48033         this.totalWidth = null;
48034         if(!suppressEvent){
48035              this.fireEvent("widthchange", this, col, width);
48036         }
48037     },
48038
48039     /**
48040      * Returns the total width of all columns.
48041      * @param {Boolean} includeHidden True to include hidden column widths
48042      * @return {Number}
48043      */
48044     getTotalWidth : function(includeHidden){
48045         if(!this.totalWidth){
48046             this.totalWidth = 0;
48047             for(var i = 0, len = this.config.length; i < len; i++){
48048                 if(includeHidden || !this.isHidden(i)){
48049                     this.totalWidth += this.getColumnWidth(i);
48050                 }
48051             }
48052         }
48053         return this.totalWidth;
48054     },
48055
48056     /**
48057      * Returns the header for the specified column.
48058      * @param {Number} col The column index
48059      * @return {String}
48060      */
48061     getColumnHeader : function(col){
48062         return this.config[col].header;
48063     },
48064
48065     /**
48066      * Sets the header for a column.
48067      * @param {Number} col The column index
48068      * @param {String} header The new header
48069      */
48070     setColumnHeader : function(col, header){
48071         this.config[col].header = header;
48072         this.fireEvent("headerchange", this, col, header);
48073     },
48074
48075     /**
48076      * Returns the tooltip for the specified column.
48077      * @param {Number} col The column index
48078      * @return {String}
48079      */
48080     getColumnTooltip : function(col){
48081             return this.config[col].tooltip;
48082     },
48083     /**
48084      * Sets the tooltip for a column.
48085      * @param {Number} col The column index
48086      * @param {String} tooltip The new tooltip
48087      */
48088     setColumnTooltip : function(col, tooltip){
48089             this.config[col].tooltip = tooltip;
48090     },
48091
48092     /**
48093      * Returns the dataIndex for the specified column.
48094      * @param {Number} col The column index
48095      * @return {Number}
48096      */
48097     getDataIndex : function(col){
48098         return this.config[col].dataIndex;
48099     },
48100
48101     /**
48102      * Sets the dataIndex for a column.
48103      * @param {Number} col The column index
48104      * @param {Number} dataIndex The new dataIndex
48105      */
48106     setDataIndex : function(col, dataIndex){
48107         this.config[col].dataIndex = dataIndex;
48108     },
48109
48110     
48111     
48112     /**
48113      * Returns true if the cell is editable.
48114      * @param {Number} colIndex The column index
48115      * @param {Number} rowIndex The row index
48116      * @return {Boolean}
48117      */
48118     isCellEditable : function(colIndex, rowIndex){
48119         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48120     },
48121
48122     /**
48123      * Returns the editor defined for the cell/column.
48124      * return false or null to disable editing.
48125      * @param {Number} colIndex The column index
48126      * @param {Number} rowIndex The row index
48127      * @return {Object}
48128      */
48129     getCellEditor : function(colIndex, rowIndex){
48130         return this.config[colIndex].editor;
48131     },
48132
48133     /**
48134      * Sets if a column is editable.
48135      * @param {Number} col The column index
48136      * @param {Boolean} editable True if the column is editable
48137      */
48138     setEditable : function(col, editable){
48139         this.config[col].editable = editable;
48140     },
48141
48142
48143     /**
48144      * Returns true if the column is hidden.
48145      * @param {Number} colIndex The column index
48146      * @return {Boolean}
48147      */
48148     isHidden : function(colIndex){
48149         return this.config[colIndex].hidden;
48150     },
48151
48152
48153     /**
48154      * Returns true if the column width cannot be changed
48155      */
48156     isFixed : function(colIndex){
48157         return this.config[colIndex].fixed;
48158     },
48159
48160     /**
48161      * Returns true if the column can be resized
48162      * @return {Boolean}
48163      */
48164     isResizable : function(colIndex){
48165         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48166     },
48167     /**
48168      * Sets if a column is hidden.
48169      * @param {Number} colIndex The column index
48170      * @param {Boolean} hidden True if the column is hidden
48171      */
48172     setHidden : function(colIndex, hidden){
48173         this.config[colIndex].hidden = hidden;
48174         this.totalWidth = null;
48175         this.fireEvent("hiddenchange", this, colIndex, hidden);
48176     },
48177
48178     /**
48179      * Sets the editor for a column.
48180      * @param {Number} col The column index
48181      * @param {Object} editor The editor object
48182      */
48183     setEditor : function(col, editor){
48184         this.config[col].editor = editor;
48185     }
48186 });
48187
48188 Roo.grid.ColumnModel.defaultRenderer = function(value){
48189         if(typeof value == "string" && value.length < 1){
48190             return "&#160;";
48191         }
48192         return value;
48193 };
48194
48195 // Alias for backwards compatibility
48196 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48197 /*
48198  * Based on:
48199  * Ext JS Library 1.1.1
48200  * Copyright(c) 2006-2007, Ext JS, LLC.
48201  *
48202  * Originally Released Under LGPL - original licence link has changed is not relivant.
48203  *
48204  * Fork - LGPL
48205  * <script type="text/javascript">
48206  */
48207
48208 /**
48209  * @class Roo.grid.AbstractSelectionModel
48210  * @extends Roo.util.Observable
48211  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48212  * implemented by descendant classes.  This class should not be directly instantiated.
48213  * @constructor
48214  */
48215 Roo.grid.AbstractSelectionModel = function(){
48216     this.locked = false;
48217     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48218 };
48219
48220 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48221     /** @ignore Called by the grid automatically. Do not call directly. */
48222     init : function(grid){
48223         this.grid = grid;
48224         this.initEvents();
48225     },
48226
48227     /**
48228      * Locks the selections.
48229      */
48230     lock : function(){
48231         this.locked = true;
48232     },
48233
48234     /**
48235      * Unlocks the selections.
48236      */
48237     unlock : function(){
48238         this.locked = false;
48239     },
48240
48241     /**
48242      * Returns true if the selections are locked.
48243      * @return {Boolean}
48244      */
48245     isLocked : function(){
48246         return this.locked;
48247     }
48248 });/*
48249  * Based on:
48250  * Ext JS Library 1.1.1
48251  * Copyright(c) 2006-2007, Ext JS, LLC.
48252  *
48253  * Originally Released Under LGPL - original licence link has changed is not relivant.
48254  *
48255  * Fork - LGPL
48256  * <script type="text/javascript">
48257  */
48258 /**
48259  * @extends Roo.grid.AbstractSelectionModel
48260  * @class Roo.grid.RowSelectionModel
48261  * The default SelectionModel used by {@link Roo.grid.Grid}.
48262  * It supports multiple selections and keyboard selection/navigation. 
48263  * @constructor
48264  * @param {Object} config
48265  */
48266 Roo.grid.RowSelectionModel = function(config){
48267     Roo.apply(this, config);
48268     this.selections = new Roo.util.MixedCollection(false, function(o){
48269         return o.id;
48270     });
48271
48272     this.last = false;
48273     this.lastActive = false;
48274
48275     this.addEvents({
48276         /**
48277              * @event selectionchange
48278              * Fires when the selection changes
48279              * @param {SelectionModel} this
48280              */
48281             "selectionchange" : true,
48282         /**
48283              * @event afterselectionchange
48284              * Fires after the selection changes (eg. by key press or clicking)
48285              * @param {SelectionModel} this
48286              */
48287             "afterselectionchange" : true,
48288         /**
48289              * @event beforerowselect
48290              * Fires when a row is selected being selected, return false to cancel.
48291              * @param {SelectionModel} this
48292              * @param {Number} rowIndex The selected index
48293              * @param {Boolean} keepExisting False if other selections will be cleared
48294              */
48295             "beforerowselect" : true,
48296         /**
48297              * @event rowselect
48298              * Fires when a row is selected.
48299              * @param {SelectionModel} this
48300              * @param {Number} rowIndex The selected index
48301              * @param {Roo.data.Record} r The record
48302              */
48303             "rowselect" : true,
48304         /**
48305              * @event rowdeselect
48306              * Fires when a row is deselected.
48307              * @param {SelectionModel} this
48308              * @param {Number} rowIndex The selected index
48309              */
48310         "rowdeselect" : true
48311     });
48312     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48313     this.locked = false;
48314 };
48315
48316 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48317     /**
48318      * @cfg {Boolean} singleSelect
48319      * True to allow selection of only one row at a time (defaults to false)
48320      */
48321     singleSelect : false,
48322
48323     // private
48324     initEvents : function(){
48325
48326         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48327             this.grid.on("mousedown", this.handleMouseDown, this);
48328         }else{ // allow click to work like normal
48329             this.grid.on("rowclick", this.handleDragableRowClick, this);
48330         }
48331
48332         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48333             "up" : function(e){
48334                 if(!e.shiftKey){
48335                     this.selectPrevious(e.shiftKey);
48336                 }else if(this.last !== false && this.lastActive !== false){
48337                     var last = this.last;
48338                     this.selectRange(this.last,  this.lastActive-1);
48339                     this.grid.getView().focusRow(this.lastActive);
48340                     if(last !== false){
48341                         this.last = last;
48342                     }
48343                 }else{
48344                     this.selectFirstRow();
48345                 }
48346                 this.fireEvent("afterselectionchange", this);
48347             },
48348             "down" : function(e){
48349                 if(!e.shiftKey){
48350                     this.selectNext(e.shiftKey);
48351                 }else if(this.last !== false && this.lastActive !== false){
48352                     var last = this.last;
48353                     this.selectRange(this.last,  this.lastActive+1);
48354                     this.grid.getView().focusRow(this.lastActive);
48355                     if(last !== false){
48356                         this.last = last;
48357                     }
48358                 }else{
48359                     this.selectFirstRow();
48360                 }
48361                 this.fireEvent("afterselectionchange", this);
48362             },
48363             scope: this
48364         });
48365
48366         var view = this.grid.view;
48367         view.on("refresh", this.onRefresh, this);
48368         view.on("rowupdated", this.onRowUpdated, this);
48369         view.on("rowremoved", this.onRemove, this);
48370     },
48371
48372     // private
48373     onRefresh : function(){
48374         var ds = this.grid.dataSource, i, v = this.grid.view;
48375         var s = this.selections;
48376         s.each(function(r){
48377             if((i = ds.indexOfId(r.id)) != -1){
48378                 v.onRowSelect(i);
48379             }else{
48380                 s.remove(r);
48381             }
48382         });
48383     },
48384
48385     // private
48386     onRemove : function(v, index, r){
48387         this.selections.remove(r);
48388     },
48389
48390     // private
48391     onRowUpdated : function(v, index, r){
48392         if(this.isSelected(r)){
48393             v.onRowSelect(index);
48394         }
48395     },
48396
48397     /**
48398      * Select records.
48399      * @param {Array} records The records to select
48400      * @param {Boolean} keepExisting (optional) True to keep existing selections
48401      */
48402     selectRecords : function(records, keepExisting){
48403         if(!keepExisting){
48404             this.clearSelections();
48405         }
48406         var ds = this.grid.dataSource;
48407         for(var i = 0, len = records.length; i < len; i++){
48408             this.selectRow(ds.indexOf(records[i]), true);
48409         }
48410     },
48411
48412     /**
48413      * Gets the number of selected rows.
48414      * @return {Number}
48415      */
48416     getCount : function(){
48417         return this.selections.length;
48418     },
48419
48420     /**
48421      * Selects the first row in the grid.
48422      */
48423     selectFirstRow : function(){
48424         this.selectRow(0);
48425     },
48426
48427     /**
48428      * Select the last row.
48429      * @param {Boolean} keepExisting (optional) True to keep existing selections
48430      */
48431     selectLastRow : function(keepExisting){
48432         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48433     },
48434
48435     /**
48436      * Selects the row immediately following the last selected row.
48437      * @param {Boolean} keepExisting (optional) True to keep existing selections
48438      */
48439     selectNext : function(keepExisting){
48440         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48441             this.selectRow(this.last+1, keepExisting);
48442             this.grid.getView().focusRow(this.last);
48443         }
48444     },
48445
48446     /**
48447      * Selects the row that precedes the last selected row.
48448      * @param {Boolean} keepExisting (optional) True to keep existing selections
48449      */
48450     selectPrevious : function(keepExisting){
48451         if(this.last){
48452             this.selectRow(this.last-1, keepExisting);
48453             this.grid.getView().focusRow(this.last);
48454         }
48455     },
48456
48457     /**
48458      * Returns the selected records
48459      * @return {Array} Array of selected records
48460      */
48461     getSelections : function(){
48462         return [].concat(this.selections.items);
48463     },
48464
48465     /**
48466      * Returns the first selected record.
48467      * @return {Record}
48468      */
48469     getSelected : function(){
48470         return this.selections.itemAt(0);
48471     },
48472
48473
48474     /**
48475      * Clears all selections.
48476      */
48477     clearSelections : function(fast){
48478         if(this.locked) return;
48479         if(fast !== true){
48480             var ds = this.grid.dataSource;
48481             var s = this.selections;
48482             s.each(function(r){
48483                 this.deselectRow(ds.indexOfId(r.id));
48484             }, this);
48485             s.clear();
48486         }else{
48487             this.selections.clear();
48488         }
48489         this.last = false;
48490     },
48491
48492
48493     /**
48494      * Selects all rows.
48495      */
48496     selectAll : function(){
48497         if(this.locked) return;
48498         this.selections.clear();
48499         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48500             this.selectRow(i, true);
48501         }
48502     },
48503
48504     /**
48505      * Returns True if there is a selection.
48506      * @return {Boolean}
48507      */
48508     hasSelection : function(){
48509         return this.selections.length > 0;
48510     },
48511
48512     /**
48513      * Returns True if the specified row is selected.
48514      * @param {Number/Record} record The record or index of the record to check
48515      * @return {Boolean}
48516      */
48517     isSelected : function(index){
48518         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48519         return (r && this.selections.key(r.id) ? true : false);
48520     },
48521
48522     /**
48523      * Returns True if the specified record id is selected.
48524      * @param {String} id The id of record to check
48525      * @return {Boolean}
48526      */
48527     isIdSelected : function(id){
48528         return (this.selections.key(id) ? true : false);
48529     },
48530
48531     // private
48532     handleMouseDown : function(e, t){
48533         var view = this.grid.getView(), rowIndex;
48534         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48535             return;
48536         };
48537         if(e.shiftKey && this.last !== false){
48538             var last = this.last;
48539             this.selectRange(last, rowIndex, e.ctrlKey);
48540             this.last = last; // reset the last
48541             view.focusRow(rowIndex);
48542         }else{
48543             var isSelected = this.isSelected(rowIndex);
48544             if(e.button !== 0 && isSelected){
48545                 view.focusRow(rowIndex);
48546             }else if(e.ctrlKey && isSelected){
48547                 this.deselectRow(rowIndex);
48548             }else if(!isSelected){
48549                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48550                 view.focusRow(rowIndex);
48551             }
48552         }
48553         this.fireEvent("afterselectionchange", this);
48554     },
48555     // private
48556     handleDragableRowClick :  function(grid, rowIndex, e) 
48557     {
48558         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48559             this.selectRow(rowIndex, false);
48560             grid.view.focusRow(rowIndex);
48561              this.fireEvent("afterselectionchange", this);
48562         }
48563     },
48564     
48565     /**
48566      * Selects multiple rows.
48567      * @param {Array} rows Array of the indexes of the row to select
48568      * @param {Boolean} keepExisting (optional) True to keep existing selections
48569      */
48570     selectRows : function(rows, keepExisting){
48571         if(!keepExisting){
48572             this.clearSelections();
48573         }
48574         for(var i = 0, len = rows.length; i < len; i++){
48575             this.selectRow(rows[i], true);
48576         }
48577     },
48578
48579     /**
48580      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48581      * @param {Number} startRow The index of the first row in the range
48582      * @param {Number} endRow The index of the last row in the range
48583      * @param {Boolean} keepExisting (optional) True to retain existing selections
48584      */
48585     selectRange : function(startRow, endRow, keepExisting){
48586         if(this.locked) return;
48587         if(!keepExisting){
48588             this.clearSelections();
48589         }
48590         if(startRow <= endRow){
48591             for(var i = startRow; i <= endRow; i++){
48592                 this.selectRow(i, true);
48593             }
48594         }else{
48595             for(var i = startRow; i >= endRow; i--){
48596                 this.selectRow(i, true);
48597             }
48598         }
48599     },
48600
48601     /**
48602      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48603      * @param {Number} startRow The index of the first row in the range
48604      * @param {Number} endRow The index of the last row in the range
48605      */
48606     deselectRange : function(startRow, endRow, preventViewNotify){
48607         if(this.locked) return;
48608         for(var i = startRow; i <= endRow; i++){
48609             this.deselectRow(i, preventViewNotify);
48610         }
48611     },
48612
48613     /**
48614      * Selects a row.
48615      * @param {Number} row The index of the row to select
48616      * @param {Boolean} keepExisting (optional) True to keep existing selections
48617      */
48618     selectRow : function(index, keepExisting, preventViewNotify){
48619         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48620         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48621             if(!keepExisting || this.singleSelect){
48622                 this.clearSelections();
48623             }
48624             var r = this.grid.dataSource.getAt(index);
48625             this.selections.add(r);
48626             this.last = this.lastActive = index;
48627             if(!preventViewNotify){
48628                 this.grid.getView().onRowSelect(index);
48629             }
48630             this.fireEvent("rowselect", this, index, r);
48631             this.fireEvent("selectionchange", this);
48632         }
48633     },
48634
48635     /**
48636      * Deselects a row.
48637      * @param {Number} row The index of the row to deselect
48638      */
48639     deselectRow : function(index, preventViewNotify){
48640         if(this.locked) return;
48641         if(this.last == index){
48642             this.last = false;
48643         }
48644         if(this.lastActive == index){
48645             this.lastActive = false;
48646         }
48647         var r = this.grid.dataSource.getAt(index);
48648         this.selections.remove(r);
48649         if(!preventViewNotify){
48650             this.grid.getView().onRowDeselect(index);
48651         }
48652         this.fireEvent("rowdeselect", this, index);
48653         this.fireEvent("selectionchange", this);
48654     },
48655
48656     // private
48657     restoreLast : function(){
48658         if(this._last){
48659             this.last = this._last;
48660         }
48661     },
48662
48663     // private
48664     acceptsNav : function(row, col, cm){
48665         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48666     },
48667
48668     // private
48669     onEditorKey : function(field, e){
48670         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48671         if(k == e.TAB){
48672             e.stopEvent();
48673             ed.completeEdit();
48674             if(e.shiftKey){
48675                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48676             }else{
48677                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48678             }
48679         }else if(k == e.ENTER && !e.ctrlKey){
48680             e.stopEvent();
48681             ed.completeEdit();
48682             if(e.shiftKey){
48683                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48684             }else{
48685                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48686             }
48687         }else if(k == e.ESC){
48688             ed.cancelEdit();
48689         }
48690         if(newCell){
48691             g.startEditing(newCell[0], newCell[1]);
48692         }
48693     }
48694 });/*
48695  * Based on:
48696  * Ext JS Library 1.1.1
48697  * Copyright(c) 2006-2007, Ext JS, LLC.
48698  *
48699  * Originally Released Under LGPL - original licence link has changed is not relivant.
48700  *
48701  * Fork - LGPL
48702  * <script type="text/javascript">
48703  */
48704 /**
48705  * @class Roo.grid.CellSelectionModel
48706  * @extends Roo.grid.AbstractSelectionModel
48707  * This class provides the basic implementation for cell selection in a grid.
48708  * @constructor
48709  * @param {Object} config The object containing the configuration of this model.
48710  */
48711 Roo.grid.CellSelectionModel = function(config){
48712     Roo.apply(this, config);
48713
48714     this.selection = null;
48715
48716     this.addEvents({
48717         /**
48718              * @event beforerowselect
48719              * Fires before a cell is selected.
48720              * @param {SelectionModel} this
48721              * @param {Number} rowIndex The selected row index
48722              * @param {Number} colIndex The selected cell index
48723              */
48724             "beforecellselect" : true,
48725         /**
48726              * @event cellselect
48727              * Fires when a cell is selected.
48728              * @param {SelectionModel} this
48729              * @param {Number} rowIndex The selected row index
48730              * @param {Number} colIndex The selected cell index
48731              */
48732             "cellselect" : true,
48733         /**
48734              * @event selectionchange
48735              * Fires when the active selection changes.
48736              * @param {SelectionModel} this
48737              * @param {Object} selection null for no selection or an object (o) with two properties
48738                 <ul>
48739                 <li>o.record: the record object for the row the selection is in</li>
48740                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
48741                 </ul>
48742              */
48743             "selectionchange" : true
48744     });
48745     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
48746 };
48747
48748 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
48749
48750     /** @ignore */
48751     initEvents : function(){
48752         this.grid.on("mousedown", this.handleMouseDown, this);
48753         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
48754         var view = this.grid.view;
48755         view.on("refresh", this.onViewChange, this);
48756         view.on("rowupdated", this.onRowUpdated, this);
48757         view.on("beforerowremoved", this.clearSelections, this);
48758         view.on("beforerowsinserted", this.clearSelections, this);
48759         if(this.grid.isEditor){
48760             this.grid.on("beforeedit", this.beforeEdit,  this);
48761         }
48762     },
48763
48764         //private
48765     beforeEdit : function(e){
48766         this.select(e.row, e.column, false, true, e.record);
48767     },
48768
48769         //private
48770     onRowUpdated : function(v, index, r){
48771         if(this.selection && this.selection.record == r){
48772             v.onCellSelect(index, this.selection.cell[1]);
48773         }
48774     },
48775
48776         //private
48777     onViewChange : function(){
48778         this.clearSelections(true);
48779     },
48780
48781         /**
48782          * Returns the currently selected cell,.
48783          * @return {Array} The selected cell (row, column) or null if none selected.
48784          */
48785     getSelectedCell : function(){
48786         return this.selection ? this.selection.cell : null;
48787     },
48788
48789     /**
48790      * Clears all selections.
48791      * @param {Boolean} true to prevent the gridview from being notified about the change.
48792      */
48793     clearSelections : function(preventNotify){
48794         var s = this.selection;
48795         if(s){
48796             if(preventNotify !== true){
48797                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
48798             }
48799             this.selection = null;
48800             this.fireEvent("selectionchange", this, null);
48801         }
48802     },
48803
48804     /**
48805      * Returns true if there is a selection.
48806      * @return {Boolean}
48807      */
48808     hasSelection : function(){
48809         return this.selection ? true : false;
48810     },
48811
48812     /** @ignore */
48813     handleMouseDown : function(e, t){
48814         var v = this.grid.getView();
48815         if(this.isLocked()){
48816             return;
48817         };
48818         var row = v.findRowIndex(t);
48819         var cell = v.findCellIndex(t);
48820         if(row !== false && cell !== false){
48821             this.select(row, cell);
48822         }
48823     },
48824
48825     /**
48826      * Selects a cell.
48827      * @param {Number} rowIndex
48828      * @param {Number} collIndex
48829      */
48830     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
48831         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
48832             this.clearSelections();
48833             r = r || this.grid.dataSource.getAt(rowIndex);
48834             this.selection = {
48835                 record : r,
48836                 cell : [rowIndex, colIndex]
48837             };
48838             if(!preventViewNotify){
48839                 var v = this.grid.getView();
48840                 v.onCellSelect(rowIndex, colIndex);
48841                 if(preventFocus !== true){
48842                     v.focusCell(rowIndex, colIndex);
48843                 }
48844             }
48845             this.fireEvent("cellselect", this, rowIndex, colIndex);
48846             this.fireEvent("selectionchange", this, this.selection);
48847         }
48848     },
48849
48850         //private
48851     isSelectable : function(rowIndex, colIndex, cm){
48852         return !cm.isHidden(colIndex);
48853     },
48854
48855     /** @ignore */
48856     handleKeyDown : function(e){
48857         if(!e.isNavKeyPress()){
48858             return;
48859         }
48860         var g = this.grid, s = this.selection;
48861         if(!s){
48862             e.stopEvent();
48863             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
48864             if(cell){
48865                 this.select(cell[0], cell[1]);
48866             }
48867             return;
48868         }
48869         var sm = this;
48870         var walk = function(row, col, step){
48871             return g.walkCells(row, col, step, sm.isSelectable,  sm);
48872         };
48873         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
48874         var newCell;
48875
48876         switch(k){
48877              case e.TAB:
48878                  if(e.shiftKey){
48879                      newCell = walk(r, c-1, -1);
48880                  }else{
48881                      newCell = walk(r, c+1, 1);
48882                  }
48883              break;
48884              case e.DOWN:
48885                  newCell = walk(r+1, c, 1);
48886              break;
48887              case e.UP:
48888                  newCell = walk(r-1, c, -1);
48889              break;
48890              case e.RIGHT:
48891                  newCell = walk(r, c+1, 1);
48892              break;
48893              case e.LEFT:
48894                  newCell = walk(r, c-1, -1);
48895              break;
48896              case e.ENTER:
48897                  if(g.isEditor && !g.editing){
48898                     g.startEditing(r, c);
48899                     e.stopEvent();
48900                     return;
48901                 }
48902              break;
48903         };
48904         if(newCell){
48905             this.select(newCell[0], newCell[1]);
48906             e.stopEvent();
48907         }
48908     },
48909
48910     acceptsNav : function(row, col, cm){
48911         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48912     },
48913
48914     onEditorKey : function(field, e){
48915         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48916         if(k == e.TAB){
48917             if(e.shiftKey){
48918                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48919             }else{
48920                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48921             }
48922             e.stopEvent();
48923         }else if(k == e.ENTER && !e.ctrlKey){
48924             ed.completeEdit();
48925             e.stopEvent();
48926         }else if(k == e.ESC){
48927             ed.cancelEdit();
48928         }
48929         if(newCell){
48930             g.startEditing(newCell[0], newCell[1]);
48931         }
48932     }
48933 });/*
48934  * Based on:
48935  * Ext JS Library 1.1.1
48936  * Copyright(c) 2006-2007, Ext JS, LLC.
48937  *
48938  * Originally Released Under LGPL - original licence link has changed is not relivant.
48939  *
48940  * Fork - LGPL
48941  * <script type="text/javascript">
48942  */
48943  
48944 /**
48945  * @class Roo.grid.EditorGrid
48946  * @extends Roo.grid.Grid
48947  * Class for creating and editable grid.
48948  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
48949  * The container MUST have some type of size defined for the grid to fill. The container will be 
48950  * automatically set to position relative if it isn't already.
48951  * @param {Object} dataSource The data model to bind to
48952  * @param {Object} colModel The column model with info about this grid's columns
48953  */
48954 Roo.grid.EditorGrid = function(container, config){
48955     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
48956     this.getGridEl().addClass("xedit-grid");
48957
48958     if(!this.selModel){
48959         this.selModel = new Roo.grid.CellSelectionModel();
48960     }
48961
48962     this.activeEditor = null;
48963
48964         this.addEvents({
48965             /**
48966              * @event beforeedit
48967              * Fires before cell editing is triggered. The edit event object has the following properties <br />
48968              * <ul style="padding:5px;padding-left:16px;">
48969              * <li>grid - This grid</li>
48970              * <li>record - The record being edited</li>
48971              * <li>field - The field name being edited</li>
48972              * <li>value - The value for the field being edited.</li>
48973              * <li>row - The grid row index</li>
48974              * <li>column - The grid column index</li>
48975              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
48976              * </ul>
48977              * @param {Object} e An edit event (see above for description)
48978              */
48979             "beforeedit" : true,
48980             /**
48981              * @event afteredit
48982              * Fires after a cell is edited. <br />
48983              * <ul style="padding:5px;padding-left:16px;">
48984              * <li>grid - This grid</li>
48985              * <li>record - The record being edited</li>
48986              * <li>field - The field name being edited</li>
48987              * <li>value - The value being set</li>
48988              * <li>originalValue - The original value for the field, before the edit.</li>
48989              * <li>row - The grid row index</li>
48990              * <li>column - The grid column index</li>
48991              * </ul>
48992              * @param {Object} e An edit event (see above for description)
48993              */
48994             "afteredit" : true,
48995             /**
48996              * @event validateedit
48997              * Fires after a cell is edited, but before the value is set in the record. 
48998          * You can use this to modify the value being set in the field, Return false
48999              * to cancel the change. The edit event object has the following properties <br />
49000              * <ul style="padding:5px;padding-left:16px;">
49001          * <li>editor - This editor</li>
49002              * <li>grid - This grid</li>
49003              * <li>record - The record being edited</li>
49004              * <li>field - The field name being edited</li>
49005              * <li>value - The value being set</li>
49006              * <li>originalValue - The original value for the field, before the edit.</li>
49007              * <li>row - The grid row index</li>
49008              * <li>column - The grid column index</li>
49009              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49010              * </ul>
49011              * @param {Object} e An edit event (see above for description)
49012              */
49013             "validateedit" : true
49014         });
49015     this.on("bodyscroll", this.stopEditing,  this);
49016     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49017 };
49018
49019 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49020     /**
49021      * @cfg {Number} clicksToEdit
49022      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49023      */
49024     clicksToEdit: 2,
49025
49026     // private
49027     isEditor : true,
49028     // private
49029     trackMouseOver: false, // causes very odd FF errors
49030
49031     onCellDblClick : function(g, row, col){
49032         this.startEditing(row, col);
49033     },
49034
49035     onEditComplete : function(ed, value, startValue){
49036         this.editing = false;
49037         this.activeEditor = null;
49038         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49039         var r = ed.record;
49040         var field = this.colModel.getDataIndex(ed.col);
49041         var e = {
49042             grid: this,
49043             record: r,
49044             field: field,
49045             originalValue: startValue,
49046             value: value,
49047             row: ed.row,
49048             column: ed.col,
49049             cancel:false,
49050             editor: ed
49051         };
49052         if(String(value) !== String(startValue)){
49053             
49054             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49055                 r.set(field, e.value);
49056                 delete e.cancel; //?? why!!!
49057                 this.fireEvent("afteredit", e);
49058             }
49059         } else {
49060             this.fireEvent("afteredit", e); // always fir it!
49061         }
49062         this.view.focusCell(ed.row, ed.col);
49063     },
49064
49065     /**
49066      * Starts editing the specified for the specified row/column
49067      * @param {Number} rowIndex
49068      * @param {Number} colIndex
49069      */
49070     startEditing : function(row, col){
49071         this.stopEditing();
49072         if(this.colModel.isCellEditable(col, row)){
49073             this.view.ensureVisible(row, col, true);
49074             var r = this.dataSource.getAt(row);
49075             var field = this.colModel.getDataIndex(col);
49076             var e = {
49077                 grid: this,
49078                 record: r,
49079                 field: field,
49080                 value: r.data[field],
49081                 row: row,
49082                 column: col,
49083                 cancel:false
49084             };
49085             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49086                 this.editing = true;
49087                 var ed = this.colModel.getCellEditor(col, row);
49088                 
49089                 if (!ed) {
49090                     return;
49091                 }
49092                 if(!ed.rendered){
49093                     ed.render(ed.parentEl || document.body);
49094                 }
49095                 ed.field.reset();
49096                 (function(){ // complex but required for focus issues in safari, ie and opera
49097                     ed.row = row;
49098                     ed.col = col;
49099                     ed.record = r;
49100                     ed.on("complete", this.onEditComplete, this, {single: true});
49101                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49102                     this.activeEditor = ed;
49103                     var v = r.data[field];
49104                     ed.startEdit(this.view.getCell(row, col), v);
49105                 }).defer(50, this);
49106             }
49107         }
49108     },
49109         
49110     /**
49111      * Stops any active editing
49112      */
49113     stopEditing : function(){
49114         if(this.activeEditor){
49115             this.activeEditor.completeEdit();
49116         }
49117         this.activeEditor = null;
49118     }
49119 });/*
49120  * Based on:
49121  * Ext JS Library 1.1.1
49122  * Copyright(c) 2006-2007, Ext JS, LLC.
49123  *
49124  * Originally Released Under LGPL - original licence link has changed is not relivant.
49125  *
49126  * Fork - LGPL
49127  * <script type="text/javascript">
49128  */
49129
49130 // private - not really -- you end up using it !
49131 // This is a support class used internally by the Grid components
49132
49133 /**
49134  * @class Roo.grid.GridEditor
49135  * @extends Roo.Editor
49136  * Class for creating and editable grid elements.
49137  * @param {Object} config any settings (must include field)
49138  */
49139 Roo.grid.GridEditor = function(field, config){
49140     if (!config && field.field) {
49141         config = field;
49142         field = Roo.factory(config.field, Roo.form);
49143     }
49144     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
49145     field.monitorTab = false;
49146 };
49147
49148 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
49149     
49150     /**
49151      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
49152      */
49153     
49154     alignment: "tl-tl",
49155     autoSize: "width",
49156     hideEl : false,
49157     cls: "x-small-editor x-grid-editor",
49158     shim:false,
49159     shadow:"frame"
49160 });/*
49161  * Based on:
49162  * Ext JS Library 1.1.1
49163  * Copyright(c) 2006-2007, Ext JS, LLC.
49164  *
49165  * Originally Released Under LGPL - original licence link has changed is not relivant.
49166  *
49167  * Fork - LGPL
49168  * <script type="text/javascript">
49169  */
49170   
49171
49172   
49173 Roo.grid.PropertyRecord = Roo.data.Record.create([
49174     {name:'name',type:'string'},  'value'
49175 ]);
49176
49177
49178 Roo.grid.PropertyStore = function(grid, source){
49179     this.grid = grid;
49180     this.store = new Roo.data.Store({
49181         recordType : Roo.grid.PropertyRecord
49182     });
49183     this.store.on('update', this.onUpdate,  this);
49184     if(source){
49185         this.setSource(source);
49186     }
49187     Roo.grid.PropertyStore.superclass.constructor.call(this);
49188 };
49189
49190
49191
49192 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
49193     setSource : function(o){
49194         this.source = o;
49195         this.store.removeAll();
49196         var data = [];
49197         for(var k in o){
49198             if(this.isEditableValue(o[k])){
49199                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
49200             }
49201         }
49202         this.store.loadRecords({records: data}, {}, true);
49203     },
49204
49205     onUpdate : function(ds, record, type){
49206         if(type == Roo.data.Record.EDIT){
49207             var v = record.data['value'];
49208             var oldValue = record.modified['value'];
49209             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
49210                 this.source[record.id] = v;
49211                 record.commit();
49212                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
49213             }else{
49214                 record.reject();
49215             }
49216         }
49217     },
49218
49219     getProperty : function(row){
49220        return this.store.getAt(row);
49221     },
49222
49223     isEditableValue: function(val){
49224         if(val && val instanceof Date){
49225             return true;
49226         }else if(typeof val == 'object' || typeof val == 'function'){
49227             return false;
49228         }
49229         return true;
49230     },
49231
49232     setValue : function(prop, value){
49233         this.source[prop] = value;
49234         this.store.getById(prop).set('value', value);
49235     },
49236
49237     getSource : function(){
49238         return this.source;
49239     }
49240 });
49241
49242 Roo.grid.PropertyColumnModel = function(grid, store){
49243     this.grid = grid;
49244     var g = Roo.grid;
49245     g.PropertyColumnModel.superclass.constructor.call(this, [
49246         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
49247         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
49248     ]);
49249     this.store = store;
49250     this.bselect = Roo.DomHelper.append(document.body, {
49251         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
49252             {tag: 'option', value: 'true', html: 'true'},
49253             {tag: 'option', value: 'false', html: 'false'}
49254         ]
49255     });
49256     Roo.id(this.bselect);
49257     var f = Roo.form;
49258     this.editors = {
49259         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
49260         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
49261         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
49262         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
49263         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
49264     };
49265     this.renderCellDelegate = this.renderCell.createDelegate(this);
49266     this.renderPropDelegate = this.renderProp.createDelegate(this);
49267 };
49268
49269 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
49270     
49271     
49272     nameText : 'Name',
49273     valueText : 'Value',
49274     
49275     dateFormat : 'm/j/Y',
49276     
49277     
49278     renderDate : function(dateVal){
49279         return dateVal.dateFormat(this.dateFormat);
49280     },
49281
49282     renderBool : function(bVal){
49283         return bVal ? 'true' : 'false';
49284     },
49285
49286     isCellEditable : function(colIndex, rowIndex){
49287         return colIndex == 1;
49288     },
49289
49290     getRenderer : function(col){
49291         return col == 1 ?
49292             this.renderCellDelegate : this.renderPropDelegate;
49293     },
49294
49295     renderProp : function(v){
49296         return this.getPropertyName(v);
49297     },
49298
49299     renderCell : function(val){
49300         var rv = val;
49301         if(val instanceof Date){
49302             rv = this.renderDate(val);
49303         }else if(typeof val == 'boolean'){
49304             rv = this.renderBool(val);
49305         }
49306         return Roo.util.Format.htmlEncode(rv);
49307     },
49308
49309     getPropertyName : function(name){
49310         var pn = this.grid.propertyNames;
49311         return pn && pn[name] ? pn[name] : name;
49312     },
49313
49314     getCellEditor : function(colIndex, rowIndex){
49315         var p = this.store.getProperty(rowIndex);
49316         var n = p.data['name'], val = p.data['value'];
49317         
49318         if(typeof(this.grid.customEditors[n]) == 'string'){
49319             return this.editors[this.grid.customEditors[n]];
49320         }
49321         if(typeof(this.grid.customEditors[n]) != 'undefined'){
49322             return this.grid.customEditors[n];
49323         }
49324         if(val instanceof Date){
49325             return this.editors['date'];
49326         }else if(typeof val == 'number'){
49327             return this.editors['number'];
49328         }else if(typeof val == 'boolean'){
49329             return this.editors['boolean'];
49330         }else{
49331             return this.editors['string'];
49332         }
49333     }
49334 });
49335
49336 /**
49337  * @class Roo.grid.PropertyGrid
49338  * @extends Roo.grid.EditorGrid
49339  * This class represents the  interface of a component based property grid control.
49340  * <br><br>Usage:<pre><code>
49341  var grid = new Roo.grid.PropertyGrid("my-container-id", {
49342       
49343  });
49344  // set any options
49345  grid.render();
49346  * </code></pre>
49347   
49348  * @constructor
49349  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49350  * The container MUST have some type of size defined for the grid to fill. The container will be
49351  * automatically set to position relative if it isn't already.
49352  * @param {Object} config A config object that sets properties on this grid.
49353  */
49354 Roo.grid.PropertyGrid = function(container, config){
49355     config = config || {};
49356     var store = new Roo.grid.PropertyStore(this);
49357     this.store = store;
49358     var cm = new Roo.grid.PropertyColumnModel(this, store);
49359     store.store.sort('name', 'ASC');
49360     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
49361         ds: store.store,
49362         cm: cm,
49363         enableColLock:false,
49364         enableColumnMove:false,
49365         stripeRows:false,
49366         trackMouseOver: false,
49367         clicksToEdit:1
49368     }, config));
49369     this.getGridEl().addClass('x-props-grid');
49370     this.lastEditRow = null;
49371     this.on('columnresize', this.onColumnResize, this);
49372     this.addEvents({
49373          /**
49374              * @event beforepropertychange
49375              * Fires before a property changes (return false to stop?)
49376              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49377              * @param {String} id Record Id
49378              * @param {String} newval New Value
49379          * @param {String} oldval Old Value
49380              */
49381         "beforepropertychange": true,
49382         /**
49383              * @event propertychange
49384              * Fires after a property changes
49385              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49386              * @param {String} id Record Id
49387              * @param {String} newval New Value
49388          * @param {String} oldval Old Value
49389              */
49390         "propertychange": true
49391     });
49392     this.customEditors = this.customEditors || {};
49393 };
49394 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
49395     
49396      /**
49397      * @cfg {Object} customEditors map of colnames=> custom editors.
49398      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
49399      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
49400      * false disables editing of the field.
49401          */
49402     
49403       /**
49404      * @cfg {Object} propertyNames map of property Names to their displayed value
49405          */
49406     
49407     render : function(){
49408         Roo.grid.PropertyGrid.superclass.render.call(this);
49409         this.autoSize.defer(100, this);
49410     },
49411
49412     autoSize : function(){
49413         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
49414         if(this.view){
49415             this.view.fitColumns();
49416         }
49417     },
49418
49419     onColumnResize : function(){
49420         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
49421         this.autoSize();
49422     },
49423     /**
49424      * Sets the data for the Grid
49425      * accepts a Key => Value object of all the elements avaiable.
49426      * @param {Object} data  to appear in grid.
49427      */
49428     setSource : function(source){
49429         this.store.setSource(source);
49430         //this.autoSize();
49431     },
49432     /**
49433      * Gets all the data from the grid.
49434      * @return {Object} data  data stored in grid
49435      */
49436     getSource : function(){
49437         return this.store.getSource();
49438     }
49439 });/*
49440  * Based on:
49441  * Ext JS Library 1.1.1
49442  * Copyright(c) 2006-2007, Ext JS, LLC.
49443  *
49444  * Originally Released Under LGPL - original licence link has changed is not relivant.
49445  *
49446  * Fork - LGPL
49447  * <script type="text/javascript">
49448  */
49449  
49450 /**
49451  * @class Roo.LoadMask
49452  * A simple utility class for generically masking elements while loading data.  If the element being masked has
49453  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
49454  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
49455  * element's UpdateManager load indicator and will be destroyed after the initial load.
49456  * @constructor
49457  * Create a new LoadMask
49458  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
49459  * @param {Object} config The config object
49460  */
49461 Roo.LoadMask = function(el, config){
49462     this.el = Roo.get(el);
49463     Roo.apply(this, config);
49464     if(this.store){
49465         this.store.on('beforeload', this.onBeforeLoad, this);
49466         this.store.on('load', this.onLoad, this);
49467         this.store.on('loadexception', this.onLoad, this);
49468         this.removeMask = false;
49469     }else{
49470         var um = this.el.getUpdateManager();
49471         um.showLoadIndicator = false; // disable the default indicator
49472         um.on('beforeupdate', this.onBeforeLoad, this);
49473         um.on('update', this.onLoad, this);
49474         um.on('failure', this.onLoad, this);
49475         this.removeMask = true;
49476     }
49477 };
49478
49479 Roo.LoadMask.prototype = {
49480     /**
49481      * @cfg {Boolean} removeMask
49482      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
49483      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
49484      */
49485     /**
49486      * @cfg {String} msg
49487      * The text to display in a centered loading message box (defaults to 'Loading...')
49488      */
49489     msg : 'Loading...',
49490     /**
49491      * @cfg {String} msgCls
49492      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
49493      */
49494     msgCls : 'x-mask-loading',
49495
49496     /**
49497      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
49498      * @type Boolean
49499      */
49500     disabled: false,
49501
49502     /**
49503      * Disables the mask to prevent it from being displayed
49504      */
49505     disable : function(){
49506        this.disabled = true;
49507     },
49508
49509     /**
49510      * Enables the mask so that it can be displayed
49511      */
49512     enable : function(){
49513         this.disabled = false;
49514     },
49515
49516     // private
49517     onLoad : function(){
49518         this.el.unmask(this.removeMask);
49519     },
49520
49521     // private
49522     onBeforeLoad : function(){
49523         if(!this.disabled){
49524             this.el.mask(this.msg, this.msgCls);
49525         }
49526     },
49527
49528     // private
49529     destroy : function(){
49530         if(this.store){
49531             this.store.un('beforeload', this.onBeforeLoad, this);
49532             this.store.un('load', this.onLoad, this);
49533             this.store.un('loadexception', this.onLoad, this);
49534         }else{
49535             var um = this.el.getUpdateManager();
49536             um.un('beforeupdate', this.onBeforeLoad, this);
49537             um.un('update', this.onLoad, this);
49538             um.un('failure', this.onLoad, this);
49539         }
49540     }
49541 };/*
49542  * Based on:
49543  * Ext JS Library 1.1.1
49544  * Copyright(c) 2006-2007, Ext JS, LLC.
49545  *
49546  * Originally Released Under LGPL - original licence link has changed is not relivant.
49547  *
49548  * Fork - LGPL
49549  * <script type="text/javascript">
49550  */
49551 Roo.XTemplate = function(){
49552     Roo.XTemplate.superclass.constructor.apply(this, arguments);
49553     var s = this.html;
49554
49555     s = ['<tpl>', s, '</tpl>'].join('');
49556
49557     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
49558
49559     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
49560     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
49561     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
49562     var m, id = 0;
49563     var tpls = [];
49564
49565     while(m = s.match(re)){
49566        var m2 = m[0].match(nameRe);
49567        var m3 = m[0].match(ifRe);
49568        var m4 = m[0].match(execRe);
49569        var exp = null, fn = null, exec = null;
49570        var name = m2 && m2[1] ? m2[1] : '';
49571        if(m3){
49572            exp = m3 && m3[1] ? m3[1] : null;
49573            if(exp){
49574                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
49575            }
49576        }
49577        if(m4){
49578            exp = m4 && m4[1] ? m4[1] : null;
49579            if(exp){
49580                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
49581            }
49582        }
49583        if(name){
49584            switch(name){
49585                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
49586                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
49587                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
49588            }
49589        }
49590        tpls.push({
49591             id: id,
49592             target: name,
49593             exec: exec,
49594             test: fn,
49595             body: m[1]||''
49596         });
49597        s = s.replace(m[0], '{xtpl'+ id + '}');
49598        ++id;
49599     }
49600     for(var i = tpls.length-1; i >= 0; --i){
49601         this.compileTpl(tpls[i]);
49602     }
49603     this.master = tpls[tpls.length-1];
49604     this.tpls = tpls;
49605 };
49606 Roo.extend(Roo.XTemplate, Roo.Template, {
49607
49608     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
49609
49610     applySubTemplate : function(id, values, parent){
49611         var t = this.tpls[id];
49612         if(t.test && !t.test.call(this, values, parent)){
49613             return '';
49614         }
49615         if(t.exec && t.exec.call(this, values, parent)){
49616             return '';
49617         }
49618         var vs = t.target ? t.target.call(this, values, parent) : values;
49619         parent = t.target ? values : parent;
49620         if(t.target && vs instanceof Array){
49621             var buf = [];
49622             for(var i = 0, len = vs.length; i < len; i++){
49623                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
49624             }
49625             return buf.join('');
49626         }
49627         return t.compiled.call(this, vs, parent);
49628     },
49629
49630     compileTpl : function(tpl){
49631         var fm = Roo.util.Format;
49632         var useF = this.disableFormats !== true;
49633         var sep = Roo.isGecko ? "+" : ",";
49634         var fn = function(m, name, format, args){
49635             if(name.substr(0, 4) == 'xtpl'){
49636                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
49637             }
49638             var v;
49639             if(name.indexOf('.') != -1){
49640                 v = name;
49641             }else{
49642                 v = "values['" + name + "']";
49643             }
49644             if(format && useF){
49645                 args = args ? ',' + args : "";
49646                 if(format.substr(0, 5) != "this."){
49647                     format = "fm." + format + '(';
49648                 }else{
49649                     format = 'this.call("'+ format.substr(5) + '", ';
49650                     args = ", values";
49651                 }
49652             }else{
49653                 args= ''; format = "("+v+" === undefined ? '' : ";
49654             }
49655             return "'"+ sep + format + v + args + ")"+sep+"'";
49656         };
49657         var body;
49658         // branched to use + in gecko and [].join() in others
49659         if(Roo.isGecko){
49660             body = "tpl.compiled = function(values, parent){ return '" +
49661                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
49662                     "';};";
49663         }else{
49664             body = ["tpl.compiled = function(values, parent){ return ['"];
49665             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
49666             body.push("'].join('');};");
49667             body = body.join('');
49668         }
49669         /** eval:var:zzzzzzz */
49670         eval(body);
49671         return this;
49672     },
49673
49674     applyTemplate : function(values){
49675         return this.master.compiled.call(this, values, {});
49676         var s = this.subs;
49677     },
49678
49679     apply : function(){
49680         return this.applyTemplate.apply(this, arguments);
49681     },
49682
49683     compile : function(){return this;}
49684 });
49685
49686 Roo.XTemplate.from = function(el){
49687     el = Roo.getDom(el);
49688     return new Roo.XTemplate(el.value || el.innerHTML);
49689 };/*
49690  * Original code for Roojs - LGPL
49691  * <script type="text/javascript">
49692  */
49693  
49694 /**
49695  * @class Roo.XComponent
49696  * A delayed Element creator...
49697  * 
49698  * Mypart.xyx = new Roo.XComponent({
49699
49700     parent : 'Mypart.xyz', // empty == document.element.!!
49701     order : '001',
49702     name : 'xxxx'
49703     region : 'xxxx'
49704     disabled : function() {} 
49705      
49706     tree : function() { // return an tree of xtype declared components
49707         var MODULE = this;
49708         return 
49709         {
49710             xtype : 'NestedLayoutPanel',
49711             // technicall
49712         }
49713      ]
49714  *})
49715  * @extends Roo.util.Observable
49716  * @constructor
49717  * @param cfg {Object} configuration of component
49718  * 
49719  */
49720 Roo.XComponent = function(cfg) {
49721     Roo.apply(this, cfg);
49722     this.addEvents({ 
49723         /**
49724              * @event built
49725              * Fires when this the componnt is built
49726              * @param {Roo.XComponent} c the component
49727              */
49728         'built' : true,
49729         /**
49730              * @event buildcomplete
49731              * Fires on the top level element when all elements have been built
49732              * @param {Roo.XComponent} c the top level component.
49733          */
49734         'buildcomplete' : true,
49735         
49736     });
49737     
49738     Roo.XComponent.register(this);
49739     this.modules = false;
49740     this.el = false; // where the layout goes..
49741     
49742     
49743 }
49744 Roo.extend(Roo.XComponent, Roo.util.Observable, {
49745     /**
49746      * @property el
49747      * The created element (with Roo.factory())
49748      * @type {Roo.Layout}
49749      */
49750     el  : false,
49751     
49752     /**
49753      * @property el
49754      * for BC  - use el in new code
49755      * @type {Roo.Layout}
49756      */
49757     panel : false,
49758     
49759     /**
49760      * @property layout
49761      * for BC  - use el in new code
49762      * @type {Roo.Layout}
49763      */
49764     layout : false,
49765     
49766      /**
49767      * @cfg {Function|boolean} disabled
49768      * If this module is disabled by some rule, return true from the funtion
49769      */
49770     disabled : false,
49771     
49772     /**
49773      * @cfg {String} parent 
49774      * Name of parent element which it get xtype added to..
49775      */
49776     parent: false,
49777     
49778     /**
49779      * @cfg {String} order
49780      * Used to set the order in which elements are created (usefull for multiple tabs)
49781      */
49782     
49783     order : false,
49784     /**
49785      * @cfg {String} name
49786      * String to display while loading.
49787      */
49788     name : false,
49789     /**
49790      * @cfg {Array} items
49791      * A single item array - the first element is the root of the tree..
49792      * It's done this way to stay compatible with the Xtype system...
49793      */
49794     items : false,
49795      
49796      
49797     
49798 });
49799
49800 Roo.apply(Roo.XComponent, {
49801     
49802     /**
49803      * @property  buildCompleted
49804      * True when the builder has completed building the interface.
49805      * @type Boolean
49806      */
49807     buildCompleted : false,
49808      
49809     /**
49810      * @property  topModule
49811      * the upper most module - uses document.element as it's constructor.
49812      * @type Object
49813      */
49814      
49815     topModule  : false,
49816       
49817     /**
49818      * @property  modules
49819      * array of modules to be created by registration system.
49820      * @type Roo.XComponent
49821      */
49822     
49823     modules : [],
49824       
49825     
49826     /**
49827      * Register components to be built later.
49828      *
49829      * This solves the following issues
49830      * - Building is not done on page load, but after an authentication process has occured.
49831      * - Interface elements are registered on page load
49832      * - Parent Interface elements may not be loaded before child, so this handles that..
49833      * 
49834      *
49835      * example:
49836      * 
49837      * MyApp.register({
49838           order : '000001',
49839           module : 'Pman.Tab.projectMgr',
49840           region : 'center',
49841           parent : 'Pman.layout',
49842           disabled : false,  // or use a function..
49843         })
49844      
49845      * * @param {Object} details about module
49846      */
49847     register : function(obj) {
49848         this.modules.push(obj);
49849          
49850     },
49851     /**
49852      * convert a string to an object..
49853      * 
49854      */
49855     
49856     toObject : function(str)
49857     {
49858         if (!str || typeof(str) == 'object') {
49859             return str;
49860         }
49861         var ar = str.split('.');
49862         var rt, o;
49863         rt = ar.shift();
49864             /** eval:var:o */
49865         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
49866         if (o === false) {
49867             throw "Module not found : " + str;
49868         }
49869         Roo.each(ar, function(e) {
49870             if (typeof(o[e]) == 'undefined') {
49871                 throw "Module not found : " + str;
49872             }
49873             o = o[e];
49874         });
49875         return o;
49876         
49877     },
49878     
49879     
49880     /**
49881      * move modules into their correct place in the tree..
49882      * 
49883      */
49884     preBuild : function ()
49885     {
49886         
49887         Roo.each(this.modules , function (obj)
49888         {
49889             obj.parent = this.toObject(obj.parent);
49890             
49891             if (!obj.parent) {
49892                 this.topModule = obj;
49893                 return;
49894             }
49895             
49896             if (!obj.parent.modules) {
49897                 obj.parent.modules = new Roo.util.MixedCollection(false, 
49898                     function(o) { return o.order + '' }
49899                 );
49900             }
49901             
49902             obj.parent.modules.add(obj);
49903         }, this);
49904     },
49905     
49906      /**
49907      * make a list of modules to build.
49908      * @return {Array} list of modules. 
49909      */ 
49910     
49911     buildOrder : function()
49912     {
49913         var _this = this;
49914         var cmp = function(a,b) {   
49915             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
49916         };
49917         
49918         if (!this.topModule || !this.topModule.modules) {
49919             throw "No top level modules to build";
49920         }
49921        
49922         // make a flat list in order of modules to build.
49923         var mods = [ this.topModule ];
49924         
49925         
49926         // add modules to their parents..
49927         var addMod = function(m) {
49928            // console.log(m.modKey);
49929             
49930             mods.push(m);
49931             if (m.modules) {
49932                 m.modules.keySort('ASC',  cmp );
49933                 m.modules.each(addMod);
49934             }
49935             // not sure if this is used any more..
49936             if (m.finalize) {
49937                 m.finalize.name = m.name + " (clean up) ";
49938                 mods.push(m.finalize);
49939             }
49940             
49941         }
49942         this.topModule.modules.keySort('ASC',  cmp );
49943         this.topModule.modules.each(addMod);
49944         return mods;
49945     },
49946     
49947      /**
49948      * Build the registered modules.
49949      * @param {Object} parent element.
49950      * @param {Function} optional method to call after module has been added.
49951      * 
49952      */ 
49953    
49954     build : function() 
49955     {
49956         
49957         this.preBuild();
49958         var mods = this.buildOrder();
49959       
49960         //this.allmods = mods;
49961         //console.log(mods);
49962         //return;
49963         if (!mods.length) { // should not happen
49964             throw "NO modules!!!";
49965         }
49966         
49967         
49968         
49969         // flash it up as modal - so we store the mask!?
49970         Roo.MessageBox.show({ title: 'loading' });
49971         Roo.MessageBox.show({
49972            title: "Please wait...",
49973            msg: "Building Interface...",
49974            width:450,
49975            progress:true,
49976            closable:false,
49977            modal: false
49978           
49979         });
49980         var total = mods.length;
49981         
49982         var _this = this;
49983         var progressRun = function() {
49984             if (!mods.length) {
49985                 console.log('hide?');
49986                 Roo.MessageBox.hide();
49987                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
49988                 return;    
49989             }
49990             
49991             var m = mods.shift();
49992             console.log(m);
49993             if (typeof(m) == 'function') { // not sure if this is supported any more..
49994                 m.call(this);
49995                 return progressRun.defer(10, _this);
49996             } 
49997             
49998             Roo.MessageBox.updateProgress(
49999                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50000                     " of " + total + 
50001                     (m.name ? (' - ' + m.name) : '')
50002                     );
50003             
50004          
50005             
50006             var disabled = (typeof(m.disabled) == 'function') ?
50007                 m.disabled.call(m.module.disabled) : m.disabled;    
50008             
50009             
50010             if (disabled) {
50011                 return progressRun(); // we do not update the display!
50012             }
50013             
50014             if (!m.parent) {
50015                 // it's a top level one..
50016                 var layoutbase = new Ext.BorderLayout(document.body, {
50017                
50018                     center: {
50019                          titlebar: false,
50020                          autoScroll:false,
50021                          closeOnTab: true,
50022                          tabPosition: 'top',
50023                          //resizeTabs: true,
50024                          alwaysShowTabs: true,
50025                          minTabWidth: 140
50026                     }
50027                 });
50028                 var tree = m.tree();
50029                 tree.region = 'center';
50030                 m.el = layoutbase.addxtype(tree);
50031                 m.panel = m.el;
50032                 m.layout = m.panel.layout;    
50033                 return progressRun.defer(10, _this);
50034             }
50035             
50036             var tree = m.tree();
50037             tree.region = tree.region || m.region;
50038             m.el = m.parent.el.addxtype(tree);
50039             m.fireEvent('built', m);
50040             m.panel = m.el;
50041             m.layout = m.panel.layout;    
50042             progressRun.defer(10, _this); 
50043             
50044         }
50045         progressRun.defer(1, _this);
50046      
50047         
50048         
50049     }
50050      
50051    
50052     
50053     
50054 });
50055  //<script type="text/javascript">
50056
50057
50058 /**
50059  * @class Roo.Login
50060  * @extends Roo.LayoutDialog
50061  * A generic Login Dialog..... - only one needed in theory!?!?
50062  *
50063  * Fires XComponent builder on success...
50064  * 
50065  * Sends 
50066  *    username,password, lang = for login actions.
50067  *    check = 1 for periodic checking that sesion is valid.
50068  *    passwordRequest = email request password
50069  *    logout = 1 = to logout
50070  * 
50071  * Affects: (this id="????" elements)
50072  *   loading  (removed) (used to indicate application is loading)
50073  *   loading-mask (hides) (used to hide application when it's building loading)
50074  *   
50075  * 
50076  * Usage: 
50077  *    
50078  * 
50079  * Myapp.login = Roo.Login({
50080      url: xxxx,
50081    
50082      realm : 'Myapp', 
50083      
50084      
50085      method : 'POST',
50086      
50087      
50088      * 
50089  })
50090  * 
50091  * 
50092  * 
50093  **/
50094  
50095 Roo.Login = function(cfg)
50096 {
50097     this.addEvents({
50098         'refreshed' : true,
50099     });
50100     
50101     Roo.apply(this,cfg);
50102     
50103     Roo.onReady(function() {
50104         this.onLoad();
50105     }, this);
50106     // call parent..
50107     
50108    
50109     Roo.Login.superclass.constructor.call(this, this);
50110     //this.addxtype(this.items[0]);
50111     
50112     
50113 }
50114
50115
50116 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50117     
50118     /**
50119      * @cfg {String} method
50120      * Method used to query for login details.
50121      */
50122     
50123     method : 'POST',
50124     /**
50125      * @cfg {String} url
50126      * URL to query login data. - eg. baseURL + '/Login.php'
50127      */
50128     url : '',
50129     
50130     /**
50131      * @property user
50132      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
50133      * @type {Object} 
50134      */
50135     user : false,
50136     /**
50137      * @property checkFails
50138      * Number of times we have attempted to get authentication check, and failed.
50139      * @type {Number} 
50140      */
50141     checkFails : 0,
50142       /**
50143      * @property intervalID
50144      * The window interval that does the constant login checking.
50145      * @type {Number} 
50146      */
50147     intervalID : 0,
50148     
50149     
50150     onLoad : function() // called on page load...
50151     {
50152         // load 
50153          
50154         if (Roo.get('loading')) { // clear any loading indicator..
50155             Roo.get('loading').remove();
50156         }
50157         
50158         //this.switchLang('en'); // set the language to english..
50159        
50160         this.check({
50161             success:  function(response, opts)  {  // check successfull...
50162             
50163                 var res = this.processResponse(response);
50164                 this.checkFails =0;
50165                 if (!res.success) { // error!
50166                     this.checkFails = 5;
50167                     //console.log('call failure');
50168                     return this.failure(response,opts);
50169                 }
50170                 
50171                 if (!res.data.id) { // id=0 == login failure.
50172                     return this.show();
50173                 }
50174                 
50175                               
50176                         //console.log(success);
50177                 this.fillAuth(res.data);   
50178                 this.checkFails =0;
50179                 Roo.XComponent.build();
50180             },
50181             failure : this.show
50182         });
50183         
50184     }, 
50185     
50186     
50187     check: function(cfg) // called every so often to refresh cookie etc..
50188     {
50189         if (cfg.again) { // could be undefined..
50190             this.checkFails++;
50191         } else {
50192             this.checkFails = 0;
50193         }
50194         var _this = this;
50195         if (this.sending) {
50196             if ( this.checkFails > 4) {
50197                 Roo.MessageBox.alert("Error",  
50198                     "Error getting authentication status. - try reloading, or wait a while", function() {
50199                         _this.sending = false;
50200                     }); 
50201                 return;
50202             }
50203             cfg.again = true;
50204             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
50205             return;
50206         }
50207         this.sending = true;
50208         
50209         Roo.Ajax.request({  
50210             url: this.url,
50211             params: {
50212                 getAuthUser: true
50213             },  
50214             method: this.method,
50215             success:  cfg.success || this.success,
50216             failure : cfg.failure || this.failure,
50217             scope : this,
50218             callCfg : cfg
50219               
50220         });  
50221     }, 
50222     
50223     
50224     logout: function()
50225     {
50226         window.onbeforeunload = function() { }; // false does not work for IE..
50227         this.user = false;
50228         var _this = this;
50229         
50230         Roo.Ajax.request({  
50231             url: this.url,
50232             params: {
50233                 logout: 1
50234             },  
50235             method: 'GET',
50236             failure : function() {
50237                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
50238                     document.location = document.location.toString() + '?ts=' + Math.random();
50239                 });
50240                 
50241             },
50242             success : function() {
50243                 _this.user = false;
50244                 this.checkFails =0;
50245                 // fixme..
50246                 document.location = document.location.toString() + '?ts=' + Math.random();
50247             }
50248               
50249               
50250         }); 
50251     },
50252     
50253     processResponse : function (response)
50254     {
50255         var res = '';
50256         try {
50257             res = Roo.decode(response.responseText);
50258             // oops...
50259             if (typeof(res) != 'object') {
50260                 res = { success : false, errorMsg : res, errors : true };
50261             }
50262             if (typeof(res.success) == 'undefined') {
50263                 res.success = false;
50264             }
50265             
50266         } catch(e) {
50267             res = { success : false,  errorMsg : response.responseText, errors : true };
50268         }
50269         return res;
50270     },
50271     
50272     success : function(response, opts)  // check successfull...
50273     {  
50274         this.sending = false;
50275         var res = this.processResponse(response);
50276         if (!res.success) {
50277             return this.failure(response, opts);
50278         }
50279         if (!res.data || !res.data.id) {
50280             return this.failure(response,opts);
50281         }
50282         //console.log(res);
50283         this.fillAuth(res.data);
50284         
50285         this.checkFails =0;
50286         
50287     },
50288     
50289     
50290     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
50291     {
50292         this.authUser = -1;
50293         this.sending = false;
50294         var res = this.processResponse(response);
50295         //console.log(res);
50296         if ( this.checkFails > 2) {
50297         
50298             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
50299                 "Error getting authentication status. - try reloading"); 
50300             return;
50301         }
50302         opts.callCfg.again = true;
50303         this.check.defer(1000, this, [ opts.callCfg ]);
50304         return;  
50305     },
50306     
50307     
50308     
50309     fillAuth: function(au) {
50310         this.startAuthCheck();
50311         this.authUserId = au.id;
50312         this.authUser = au;
50313         this.lastChecked = new Date();
50314         this.fireEvent('refreshed', au);
50315         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
50316         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
50317         au.lang = au.lang || 'en';
50318         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
50319         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
50320         this.switchLang(au.lang );
50321         
50322      
50323         // open system... - -on setyp..
50324         if (this.authUserId  < 0) {
50325             Roo.MessageBox.alert("Warning", 
50326                 "This is an open system - please set up a admin user with a password.");  
50327         }
50328          
50329         //Pman.onload(); // which should do nothing if it's a re-auth result...
50330         
50331              
50332     },
50333     
50334     startAuthCheck : function() // starter for timeout checking..
50335     {
50336         if (this.intervalID) { // timer already in place...
50337             return false;
50338         }
50339         var _this = this;
50340         this.intervalID =  window.setInterval(function() {
50341               _this.check(false);
50342             }, 120000); // every 120 secs = 2mins..
50343         
50344         
50345     },
50346          
50347     
50348     switchLang : function (lang) 
50349     {
50350         _T = typeof(_T) == 'undefined' ? false : _T;
50351           if (!_T || !lang.length) {
50352             return;
50353         }
50354         
50355         if (!_T && lang != 'en') {
50356             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50357             return;
50358         }
50359         
50360         if (typeof(_T.en) == 'undefined') {
50361             _T.en = {};
50362             Roo.apply(_T.en, _T);
50363         }
50364         
50365         if (typeof(_T[lang]) == 'undefined') {
50366             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50367             return;
50368         }
50369         
50370         
50371         Roo.apply(_T, _T[lang]);
50372         // just need to set the text values for everything...
50373         var _this = this;
50374         /* this will not work ...
50375         if (this.form) { 
50376             
50377                
50378             function formLabel(name, val) {
50379                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
50380             }
50381             
50382             formLabel('password', "Password"+':');
50383             formLabel('username', "Email Address"+':');
50384             formLabel('lang', "Language"+':');
50385             this.dialog.setTitle("Login");
50386             this.dialog.buttons[0].setText("Forgot Password");
50387             this.dialog.buttons[1].setText("Login");
50388         }
50389         */
50390         
50391         
50392     },
50393     
50394     
50395     title: "Login",
50396     modal: true,
50397     width:  350,
50398     //height: 230,
50399     height: 180,
50400     shadow: true,
50401     minWidth:200,
50402     minHeight:180,
50403     //proxyDrag: true,
50404     closable: false,
50405     draggable: false,
50406     collapsible: false,
50407     resizable: false,
50408     center: {  // needed??
50409         autoScroll:false,
50410         titlebar: false,
50411        // tabPosition: 'top',
50412         hideTabs: true,
50413         closeOnTab: true,
50414         alwaysShowTabs: false
50415     } ,
50416     listeners : {
50417         
50418         show  : function(dlg)
50419         {
50420             //console.log(this);
50421             this.form = this.layout.getRegion('center').activePanel.form;
50422             this.form.dialog = dlg;
50423             this.buttons[0].form = this.form;
50424             this.buttons[0].dialog = dlg
50425             this.buttons[1].form = this.form;
50426             this.buttons[1].dialog = dlg;
50427            
50428            //this.resizeToLogo.defer(1000,this);
50429             // this is all related to resizing for logos..
50430             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
50431            //// if (!sz) {
50432              //   this.resizeToLogo.defer(1000,this);
50433              //   return;
50434            // }
50435             //var w = Ext.lib.Dom.getViewWidth() - 100;
50436             //var h = Ext.lib.Dom.getViewHeight() - 100;
50437             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
50438             //this.center();
50439             if (this.disabled) {
50440                 this.hide();
50441                 return;
50442             }
50443             
50444             if (this.user.id < 0) { // used for inital setup situations.
50445                 return;
50446             }
50447             
50448             if (this.intervalID) {
50449                 // remove the timer
50450                 window.clearInterval(this.intervalID);
50451                 this.intervalID = false;
50452             }
50453             
50454             
50455             if (Roo.get('loading')) {
50456                 Roo.get('loading').remove();
50457             }
50458             if (Roo.get('loading-mask')) {
50459                 Roo.get('loading-mask').hide();
50460             }
50461             
50462             //incomming._node = tnode;
50463             this.form.reset();
50464             //this.dialog.modal = !modal;
50465             //this.dialog.show();
50466             this.el.unmask(); 
50467             
50468             
50469             this.form.setValues({
50470                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
50471                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
50472             });
50473             
50474             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
50475             if (this.form.findField('username').getValue().length > 0 ){
50476                 this.form.findField('password').focus();
50477             } else {
50478                this.form.findField('username').focus();
50479             }
50480     
50481         }
50482     },
50483     items : [
50484          {
50485        
50486             xtype : 'ContentPanel',
50487             xns : Roo,
50488             region: 'center',
50489             fitToFrame : true,
50490             
50491             items : [
50492     
50493                 {
50494                
50495                     xtype : 'Form',
50496                     xns : Roo.form,
50497                     labelWidth: 100,
50498                     style : 'margin: 10px;',
50499                     
50500                     listeners : {
50501                         actionfailed : function(f, act) {
50502                             // form can return { errors: .... }
50503                                 
50504                             //act.result.errors // invalid form element list...
50505                             //act.result.errorMsg// invalid form element list...
50506                             
50507                             this.dialog.el.unmask();
50508                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
50509                                         "Login failed - communication error - try again.");
50510                                       
50511                         },
50512                         actioncomplete: function(re, act) {
50513                              
50514                             Roo.state.Manager.set(
50515                                 this.dialog.realm + '.username',  
50516                                     this.findField('username').getValue()
50517                             );
50518                             Roo.state.Manager.set(
50519                                 this.dialog.realm + '.lang',  
50520                                 this.findField('lang').getValue() 
50521                             );
50522                             
50523                             this.dialog.fillAuth(act.result.data);
50524                               
50525                             this.dialog.hide();
50526                             
50527                             if (Roo.get('loading-mask')) {
50528                                 Roo.get('loading-mask').show();
50529                             }
50530                             Roo.XComponent.build();
50531                             
50532                              
50533                             
50534                         }
50535                     },
50536                     items : [
50537                         {
50538                             xtype : 'TextField',
50539                             xns : Roo.form,
50540                             fieldLabel: "Email Address",
50541                             name: 'username',
50542                             width:200,
50543                             autoCreate : {tag: "input", type: "text", size: "20"}
50544                         },
50545                         {
50546                             xtype : 'TextField',
50547                             xns : Roo.form,
50548                             fieldLabel: "Password",
50549                             inputType: 'password',
50550                             name: 'password',
50551                             width:200,
50552                             autoCreate : {tag: "input", type: "text", size: "20"},
50553                             listeners : {
50554                                 specialkey : function(e,ev) {
50555                                     if (ev.keyCode == 13) {
50556                                         this.form.dialog.el.mask("Logging in");
50557                                         this.form.doAction('submit', {
50558                                             url: this.form.dialog.url,
50559                                             method: this.form.dialog.method,
50560                                         });
50561                                     }
50562                                 }
50563                             }  
50564                         },
50565                         {
50566                             xtype : 'ComboBox',
50567                             xns : Roo.form,
50568                             fieldLabel: "Language",
50569                             name : 'langdisp',
50570                             store: {
50571                                 xtype : 'SimpleStore',
50572                                 fields: ['lang', 'ldisp'],
50573                                 data : [
50574                                     [ 'en', 'English' ],
50575                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
50576                                     [ 'zh_CN', '\u7C21\u4E2D' ]
50577                                 ]
50578                             },
50579                             
50580                             valueField : 'lang',
50581                             hiddenName:  'lang',
50582                             width: 200,
50583                             displayField:'ldisp',
50584                             typeAhead: false,
50585                             editable: false,
50586                             mode: 'local',
50587                             triggerAction: 'all',
50588                             emptyText:'Select a Language...',
50589                             selectOnFocus:true,
50590                             listeners : {
50591                                 select :  function(cb, rec, ix) {
50592                                     this.form.switchLang(rec.data.lang);
50593                                 }
50594                             }
50595                         
50596                         }
50597                     ]
50598                 }
50599                   
50600                 
50601             ]
50602         }
50603     ],
50604     buttons : [
50605         {
50606             xtype : 'Button',
50607             xns : 'Roo',
50608             text : "Forgot Password",
50609             listeners : {
50610                 click : function() {
50611                     //console.log(this);
50612                     var n = this.form.findField('username').getValue();
50613                     if (!n.length) {
50614                         Roo.MessageBox.alert("Error", "Fill in your email address");
50615                         return;
50616                     }
50617                     Roo.Ajax.request({
50618                         url: this.dialog.url,
50619                         params: {
50620                             passwordRequest: n
50621                         },
50622                         method: this.dialog.method,
50623                         success:  function(response, opts)  {  // check successfull...
50624                         
50625                             var res = this.dialog.processResponse(response);
50626                             if (!res.success) { // error!
50627                                Roo.MessageBox.alert("Error" ,
50628                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
50629                                return;
50630                             }
50631                             Roo.MessageBox.alert("Notice" ,
50632                                 "Please check you email for the Password Reset message");
50633                         },
50634                         failure : function() {
50635                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
50636                         }
50637                         
50638                     });
50639                 }
50640             }
50641         },
50642         {
50643             xtype : 'Button',
50644             xns : 'Roo',
50645             text : "Login",
50646             listeners : {
50647                 
50648                 click : function () {
50649                         
50650                     this.dialog.el.mask("Logging in");
50651                     this.form.doAction('submit', {
50652                             url: this.dialog.url,
50653                             method: this.dialog.method
50654                     });
50655                 }
50656             }
50657         }
50658     ]
50659   
50660   
50661 })
50662  
50663
50664
50665